revert git version

This commit is contained in:
zedy
2023-01-31 13:34:42 +08:00
parent 9955c69264
commit d27aeffeac
42 changed files with 338 additions and 236 deletions

View File

@@ -3,7 +3,8 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
FROM mcr.microsoft.com/dotnet/sdk:6.0 FROM mcr.microsoft.com/dotnet/sdk:7.0
# This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" # This Dockerfile adds a non-root user with sudo access. Use the "remoteUser"
# property in devcontainer.json to use it. On Linux, the container user's GID/UIDs # property in devcontainer.json to use it. On Linux, the container user's GID/UIDs

View File

@@ -25,15 +25,22 @@
// Comment out to connect as root user. See https://aka.ms/vscode-remote/containers/non-root. // Comment out to connect as root user. See https://aka.ms/vscode-remote/containers/non-root.
// make sure this is the same as USERNAME above // make sure this is the same as USERNAME above
"remoteUser": "vscode", "remoteUser": "vscode",
"runArgs": [
"-v",
"/var/run/docker.sock:/var/run/docker.sock"
],
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.profiles.linux": {
"bash": {
"path": "bash",
"icon": "terminal-bash"
}
}
},
// Set *default* container specific settings.json values on container create. // Add the IDs of extensions you want installed when the container is created.
"settings": { "extensions": [
"terminal.integrated.shell.linux": "/bin/bash"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-azuretools.azure-dev",
"ms-dotnettools.csharp", "ms-dotnettools.csharp",
"formulahendry.dotnet-test-explorer", "formulahendry.dotnet-test-explorer",
"ms-vscode.vscode-node-azure-pack", "ms-vscode.vscode-node-azure-pack",

View File

@@ -12,7 +12,7 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '6.0.x' dotnet-version: '7.0.x'
include-prerelease: true include-prerelease: true
- name: Build with dotnet - name: Build with dotnet

View File

@@ -12,7 +12,7 @@ jobs:
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 3.1.x dotnet-version: 7.0.x
- name: Build with dotnet - name: Build with dotnet
run: dotnet build ./eShopOnWeb.sln --configuration Release run: dotnet build ./eShopOnWeb.sln --configuration Release

2
.vscode/launch.json vendored
View File

@@ -10,7 +10,7 @@
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/Web/bin/Debug/net5.0/Web.dll", "program": "${workspaceFolder}/src/Web/bin/Debug/net7.0/Web.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}/src/Web", "cwd": "${workspaceFolder}/src/Web",
"stopAtEntry": false, "stopAtEntry": false,

68
Directory.Packages.props Normal file
View File

@@ -0,0 +1,68 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.ApiEndpoints" Version="4.0.1" />
<PackageVersion Include="Ardalis.GuardClauses" Version="4.0.1" />
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="6.1.0" />
<PackageVersion Include="Ardalis.Result" Version="4.1.0" />
<PackageVersion Include="Ardalis.Specification" Version="6.1.0" />
<PackageVersion Include="Ardalis.ListStartupServices" Version="1.1.4" />
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.1" />
<PackageVersion Include="Azure.Identity" Version="1.6.0" />
<PackageVersion Include="BlazorInputFile" Version="0.2.0" />
<PackageVersion Include="Blazored.LocalStorage" Version="4.3.0" />
<PackageVersion Include="BuildBundlerMinifier" Version="3.2.449" PrivateAssets="All" />
<PackageVersion Include="FluentValidation" Version="11.4.0" />
<PackageVersion Include="MediatR" Version="11.1.0" />
<PackageVersion Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.2" PrivateAssets="all" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="7.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="7.0.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.2" />
<PackageVersion Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
<PackageVersion Include="MinimalApi.Endpoint" Version="1.3.0" />
<PackageVersion Include="System.Net.Http.Json" Version="7.0.0" />
<PackageVersion Include="System.Security.Claims" Version="4.3.0" />
<PackageVersion Include="System.Text.Json" Version="7.0.1" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<!-- Test -->
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageVersion Include="xunit" Version="2.4.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit.runner.console" Version="2.4.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageVersion Include="MSTest.TestFramework" Version="3.0.2" />
<PackageVersion Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>
</Project>

View File

@@ -24,7 +24,7 @@ The **eShopOnWeb** sample is related to the [eShopOnContainers](https://github.c
The goal for this sample is to demonstrate some of the principles and patterns described in the [eBook](https://aka.ms/webappebook). It is not meant to be an eCommerce reference application, and as such it does not implement many features that would be obvious and/or essential to a real eCommerce application. The goal for this sample is to demonstrate some of the principles and patterns described in the [eBook](https://aka.ms/webappebook). It is not meant to be an eCommerce reference application, and as such it does not implement many features that would be obvious and/or essential to a real eCommerce application.
> ### VERSIONS > ### VERSIONS
> #### The `main` branch is currently running ASP.NET Core 6.0. > #### The `main` branch is currently running ASP.NET Core 7.0.
> #### Older versions are tagged. > #### Older versions are tagged.
## Topics (eBook TOC) ## Topics (eBook TOC)
@@ -47,7 +47,9 @@ The store's home page should look like this:
![eShopOnWeb home page screenshot](https://user-images.githubusercontent.com/782127/88414268-92d83a00-cdaa-11ea-9b4c-db67d95be039.png) ![eShopOnWeb home page screenshot](https://user-images.githubusercontent.com/782127/88414268-92d83a00-cdaa-11ea-9b4c-db67d95be039.png)
Most of the site's functionality works with just the web application running. However, the site's Admin page relies on Blazor WebAssembly running in the browser, and it must communicate with the server using the site's PublicApi web application. You'll need to also run this project. You can configure Visual Studio to start multiple projects, or just go to the PublicApi folder in a terminal window and run `dotnet run` from there. After that from the Web folder you should run `dotnet run --launch-profile Web`. Now you should be able to browse to `https://localhost:5001/`. Note that if you use this approach, you'll need to stop the application manually in order to build the solution (otherwise you'll get file locking errors). Most of the site's functionality works with just the web application running. However, the site's Admin page relies on Blazor WebAssembly running in the browser, and it must communicate with the server using the site's PublicApi web application. You'll need to also run this project. You can configure Visual Studio to start multiple projects, or just go to the PublicApi folder in a terminal window and run `dotnet run` from there. After that from the Web folder you should run `dotnet run --launch-profile Web`. Now you should be able to browse to `https://localhost:5001/`. The admin part in Blazor is accessible to `https://localhost:5001/admin`
Note that if you use this approach, you'll need to stop the application manually in order to build the solution (otherwise you'll get file locking errors).
After cloning or downloading the sample you must setup your database. After cloning or downloading the sample you must setup your database.
To use the sample with a persistent database, you will need to run its Entity Framework Core migrations before you will be able to run the app. To use the sample with a persistent database, you will need to run its Entity Framework Core migrations before you will be able to run the app.

View File

@@ -24,6 +24,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BD72BEA-EF42-4B72-8B69-12A39EC76FBA}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BD72BEA-EF42-4B72-8B69-12A39EC76FBA}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
Directory.Packages.props = Directory.Packages.props
docker-compose.override.yml = docker-compose.override.yml docker-compose.override.yml = docker-compose.override.yml
docker-compose.yml = docker-compose.yml docker-compose.yml = docker-compose.yml
.github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml .github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml
@@ -38,7 +39,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorAdmin", "src\BlazorAd
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorShared", "src\BlazorShared\BlazorShared.csproj", "{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorShared", "src\BlazorShared\BlazorShared.csproj", "{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublicApiIntegrationTests", "tests\PublicApiIntegrationTests\PublicApiIntegrationTests.csproj", "{D53EF010-8F8C-4337-A059-456E19D8AE63}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicApiIntegrationTests", "tests\PublicApiIntegrationTests\PublicApiIntegrationTests.csproj", "{D53EF010-8F8C-4337-A059-456E19D8AE63}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"version": "6.0.x", "version": "7.0.x",
"rollForward": "latestFeature" "rollForward": "latestFeature"
} }
} }

View File

@@ -1,18 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace> <RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="4.0.1" /> <PackageReference Include="Ardalis.GuardClauses" />
<PackageReference Include="Ardalis.Result" Version="4.1.0" /> <PackageReference Include="Ardalis.Result" />
<PackageReference Include="Ardalis.Specification" Version="6.1.0" /> <PackageReference Include="Ardalis.Specification" />
<PackageReference Include="MediatR" Version="10.0.1" /> <PackageReference Include="System.Security.Claims" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" /> <PackageReference Include="System.Text.Json" />
<PackageReference Include="System.Text.Json" Version="6.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,19 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.2.0" /> <PackageReference Include="Blazored.LocalStorage" />
<PackageReference Include="BlazorInputFile" Version="0.2.0" /> <PackageReference Include="BlazorInputFile" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.8" PrivateAssets="all" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.8" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Configuration" />
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" /> <PackageReference Include="System.Net.Http.Json" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -63,7 +63,7 @@ public class CustomAuthStateProvider : AuthenticationStateProvider
if (user == null || !user.IsAuthenticated) if (user == null || !user.IsAuthenticated)
{ {
return null; return new ClaimsPrincipal(new ClaimsIdentity());
} }
var identity = new ClaimsIdentity( var identity = new ClaimsIdentity(

View File

@@ -7,7 +7,7 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await HttpClient.PostAsync("Identity/Account/Logout", null); await HttpClient.PostAsync("User/Logout", null);
await new Route(JSRuntime).RouteOutside("/Identity/Account/Login"); await new Route(JSRuntime).RouteOutside("/Identity/Account/Login");
} }

View File

@@ -1,9 +1,12 @@
@inject NavigationManager Navigation @using System.Web;
@inject NavigationManager Navigation
@inject IJSRuntime JsRuntime
@code { @code {
protected override void OnInitialized() protected override void OnInitialized()
{ {
Navigation.NavigateTo($"Identity/Account/Login?returnUrl=" + var returnUrl = HttpUtility.UrlEncode($"/{Uri.EscapeDataString(Navigation.ToBaseRelativePath(Navigation.Uri))}");
$"/{Uri.EscapeDataString(Navigation.ToBaseRelativePath(Navigation.Uri))}"); JsRuntime.InvokeVoidAsync("location.replace", $"Identity/Account/Login?returnUrl={returnUrl}");
} }
} }

View File

@@ -1,14 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>BlazorShared</RootNamespace> <RootNamespace>BlazorShared</RootNamespace>
<AssemblyName>BlazorShared</AssemblyName> <AssemblyName>BlazorShared</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BlazorInputFile" Version="0.2.0" /> <PackageReference Include="BlazorInputFile" />
<PackageReference Include="FluentValidation" Version="11.2.1" /> <PackageReference Include="FluentValidation" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,17 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.Infrastructure</RootNamespace> <RootNamespace>Microsoft.eShopWeb.Infrastructure</RootNamespace>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.Specification.EntityFrameworkCore" Version="6.1.0" /> <PackageReference Include="Ardalis.Specification.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.23.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ApplicationCore\ApplicationCore.csproj" /> <ProjectReference Include="..\ApplicationCore\ApplicationCore.csproj" />

View File

@@ -13,9 +13,8 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
/// <summary> /// <summary>
/// List Catalog Brands /// List Catalog Brands
/// </summary> /// </summary>
public class CatalogBrandListEndpoint : IEndpoint<IResult> public class CatalogBrandListEndpoint : IEndpoint<IResult, IRepository<CatalogBrand>>
{ {
private IRepository<CatalogBrand> _catalogBrandRepository;
private readonly IMapper _mapper; private readonly IMapper _mapper;
public CatalogBrandListEndpoint(IMapper mapper) public CatalogBrandListEndpoint(IMapper mapper)
@@ -28,18 +27,17 @@ public class CatalogBrandListEndpoint : IEndpoint<IResult>
app.MapGet("api/catalog-brands", app.MapGet("api/catalog-brands",
async (IRepository<CatalogBrand> catalogBrandRepository) => async (IRepository<CatalogBrand> catalogBrandRepository) =>
{ {
_catalogBrandRepository = catalogBrandRepository; return await HandleAsync(catalogBrandRepository);
return await HandleAsync();
}) })
.Produces<ListCatalogBrandsResponse>() .Produces<ListCatalogBrandsResponse>()
.WithTags("CatalogBrandEndpoints"); .WithTags("CatalogBrandEndpoints");
} }
public async Task<IResult> HandleAsync() public async Task<IResult> HandleAsync(IRepository<CatalogBrand> catalogBrandRepository)
{ {
var response = new ListCatalogBrandsResponse(); var response = new ListCatalogBrandsResponse();
var items = await _catalogBrandRepository.ListAsync(); var items = await catalogBrandRepository.ListAsync();
response.CatalogBrands.AddRange(items.Select(_mapper.Map<CatalogBrandDto>)); response.CatalogBrands.AddRange(items.Select(_mapper.Map<CatalogBrandDto>));

View File

@@ -11,9 +11,8 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
/// <summary> /// <summary>
/// Get a Catalog Item by Id /// Get a Catalog Item by Id
/// </summary> /// </summary>
public class CatalogItemGetByIdEndpoint : IEndpoint<IResult, GetByIdCatalogItemRequest> public class CatalogItemGetByIdEndpoint : IEndpoint<IResult, GetByIdCatalogItemRequest, IRepository<CatalogItem>>
{ {
private IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer; private readonly IUriComposer _uriComposer;
public CatalogItemGetByIdEndpoint(IUriComposer uriComposer) public CatalogItemGetByIdEndpoint(IUriComposer uriComposer)
@@ -26,18 +25,17 @@ public class CatalogItemGetByIdEndpoint : IEndpoint<IResult, GetByIdCatalogItemR
app.MapGet("api/catalog-items/{catalogItemId}", app.MapGet("api/catalog-items/{catalogItemId}",
async (int catalogItemId, IRepository<CatalogItem> itemRepository) => async (int catalogItemId, IRepository<CatalogItem> itemRepository) =>
{ {
_itemRepository = itemRepository; return await HandleAsync(new GetByIdCatalogItemRequest(catalogItemId), itemRepository);
return await HandleAsync(new GetByIdCatalogItemRequest(catalogItemId));
}) })
.Produces<GetByIdCatalogItemResponse>() .Produces<GetByIdCatalogItemResponse>()
.WithTags("CatalogItemEndpoints"); .WithTags("CatalogItemEndpoints");
} }
public async Task<IResult> HandleAsync(GetByIdCatalogItemRequest request) public async Task<IResult> HandleAsync(GetByIdCatalogItemRequest request, IRepository<CatalogItem> itemRepository)
{ {
var response = new GetByIdCatalogItemResponse(request.CorrelationId()); var response = new GetByIdCatalogItemResponse(request.CorrelationId());
var item = await _itemRepository.GetByIdAsync(request.CatalogItemId); var item = await itemRepository.GetByIdAsync(request.CatalogItemId);
if (item is null) if (item is null)
return Results.NotFound(); return Results.NotFound();

View File

@@ -15,9 +15,8 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
/// <summary> /// <summary>
/// List Catalog Items (paged) /// List Catalog Items (paged)
/// </summary> /// </summary>
public class CatalogItemListPagedEndpoint : IEndpoint<IResult, ListPagedCatalogItemRequest> public class CatalogItemListPagedEndpoint : IEndpoint<IResult, ListPagedCatalogItemRequest, IRepository<CatalogItem>>
{ {
private IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer; private readonly IUriComposer _uriComposer;
private readonly IMapper _mapper; private readonly IMapper _mapper;
@@ -32,19 +31,19 @@ public class CatalogItemListPagedEndpoint : IEndpoint<IResult, ListPagedCatalogI
app.MapGet("api/catalog-items", app.MapGet("api/catalog-items",
async (int? pageSize, int? pageIndex, int? catalogBrandId, int? catalogTypeId, IRepository<CatalogItem> itemRepository) => async (int? pageSize, int? pageIndex, int? catalogBrandId, int? catalogTypeId, IRepository<CatalogItem> itemRepository) =>
{ {
_itemRepository = itemRepository; return await HandleAsync(new ListPagedCatalogItemRequest(pageSize, pageIndex, catalogBrandId, catalogTypeId), itemRepository);
return await HandleAsync(new ListPagedCatalogItemRequest(pageSize, pageIndex, catalogBrandId, catalogTypeId)); })
})
.Produces<ListPagedCatalogItemResponse>() .Produces<ListPagedCatalogItemResponse>()
.WithTags("CatalogItemEndpoints"); .WithTags("CatalogItemEndpoints");
} }
public async Task<IResult> HandleAsync(ListPagedCatalogItemRequest request) public async Task<IResult> HandleAsync(ListPagedCatalogItemRequest request, IRepository<CatalogItem> itemRepository)
{ {
await Task.Delay(1000);
var response = new ListPagedCatalogItemResponse(request.CorrelationId()); var response = new ListPagedCatalogItemResponse(request.CorrelationId());
var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId); var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId);
int totalItems = await _itemRepository.CountAsync(filterSpec); int totalItems = await itemRepository.CountAsync(filterSpec);
var pagedSpec = new CatalogFilterPaginatedSpecification( var pagedSpec = new CatalogFilterPaginatedSpecification(
skip: request.PageIndex.Value * request.PageSize.Value, skip: request.PageIndex.Value * request.PageSize.Value,
@@ -52,7 +51,7 @@ public class CatalogItemListPagedEndpoint : IEndpoint<IResult, ListPagedCatalogI
brandId: request.CatalogBrandId, brandId: request.CatalogBrandId,
typeId: request.CatalogTypeId); typeId: request.CatalogTypeId);
var items = await _itemRepository.ListAsync(pagedSpec); var items = await itemRepository.ListAsync(pagedSpec);
response.CatalogItems.AddRange(items.Select(_mapper.Map<CatalogItemDto>)); response.CatalogItems.AddRange(items.Select(_mapper.Map<CatalogItemDto>));
foreach (CatalogItemDto item in response.CatalogItems) foreach (CatalogItemDto item in response.CatalogItems)

View File

@@ -15,9 +15,8 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
/// <summary> /// <summary>
/// Creates a new Catalog Item /// Creates a new Catalog Item
/// </summary> /// </summary>
public class CreateCatalogItemEndpoint : IEndpoint<IResult, CreateCatalogItemRequest> public class CreateCatalogItemEndpoint : IEndpoint<IResult, CreateCatalogItemRequest, IRepository<CatalogItem>>
{ {
private IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer; private readonly IUriComposer _uriComposer;
public CreateCatalogItemEndpoint(IUriComposer uriComposer) public CreateCatalogItemEndpoint(IUriComposer uriComposer)
@@ -31,26 +30,25 @@ public class CreateCatalogItemEndpoint : IEndpoint<IResult, CreateCatalogItemReq
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async
(CreateCatalogItemRequest request, IRepository<CatalogItem> itemRepository) => (CreateCatalogItemRequest request, IRepository<CatalogItem> itemRepository) =>
{ {
_itemRepository = itemRepository; return await HandleAsync(request, itemRepository);
return await HandleAsync(request);
}) })
.Produces<CreateCatalogItemResponse>() .Produces<CreateCatalogItemResponse>()
.WithTags("CatalogItemEndpoints"); .WithTags("CatalogItemEndpoints");
} }
public async Task<IResult> HandleAsync(CreateCatalogItemRequest request) public async Task<IResult> HandleAsync(CreateCatalogItemRequest request, IRepository<CatalogItem> itemRepository)
{ {
var response = new CreateCatalogItemResponse(request.CorrelationId()); var response = new CreateCatalogItemResponse(request.CorrelationId());
var catalogItemNameSpecification = new CatalogItemNameSpecification(request.Name); var catalogItemNameSpecification = new CatalogItemNameSpecification(request.Name);
var existingCataloogItem = await _itemRepository.CountAsync(catalogItemNameSpecification); var existingCataloogItem = await itemRepository.CountAsync(catalogItemNameSpecification);
if (existingCataloogItem > 0) if (existingCataloogItem > 0)
{ {
throw new DuplicateException($"A catalogItem with name {request.Name} already exists"); 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); var newItem = new CatalogItem(request.CatalogTypeId, request.CatalogBrandId, request.Description, request.Name, request.Price, request.PictureUri);
newItem = await _itemRepository.AddAsync(newItem); newItem = await itemRepository.AddAsync(newItem);
if (newItem.Id != 0) if (newItem.Id != 0)
{ {
@@ -59,7 +57,7 @@ public class CreateCatalogItemEndpoint : IEndpoint<IResult, CreateCatalogItemReq
// In production, we recommend uploading to a blob storage and deliver the image via CDN after a verification process. // 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"); newItem.UpdatePictureUri("eCatalog-item-default.png");
await _itemRepository.UpdateAsync(newItem); await itemRepository.UpdateAsync(newItem);
} }
var dto = new CatalogItemDto var dto = new CatalogItemDto
@@ -73,6 +71,6 @@ public class CreateCatalogItemEndpoint : IEndpoint<IResult, CreateCatalogItemReq
Price = newItem.Price Price = newItem.Price
}; };
response.CatalogItem = dto; response.CatalogItem = dto;
return Results.Created($"api/catalog-items/{dto.Id}", response); return Results.Created($"api/catalog-items/{dto.Id}", response);
} }
} }

View File

@@ -13,32 +13,29 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
/// <summary> /// <summary>
/// Deletes a Catalog Item /// Deletes a Catalog Item
/// </summary> /// </summary>
public class DeleteCatalogItemEndpoint : IEndpoint<IResult, DeleteCatalogItemRequest> public class DeleteCatalogItemEndpoint : IEndpoint<IResult, DeleteCatalogItemRequest, IRepository<CatalogItem>>
{ {
private IRepository<CatalogItem> _itemRepository;
public void AddRoute(IEndpointRouteBuilder app) public void AddRoute(IEndpointRouteBuilder app)
{ {
app.MapDelete("api/catalog-items/{catalogItemId}", app.MapDelete("api/catalog-items/{catalogItemId}",
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async
(int catalogItemId, IRepository<CatalogItem> itemRepository) => (int catalogItemId, IRepository<CatalogItem> itemRepository) =>
{ {
_itemRepository = itemRepository; return await HandleAsync(new DeleteCatalogItemRequest(catalogItemId), itemRepository);
return await HandleAsync(new DeleteCatalogItemRequest(catalogItemId));
}) })
.Produces<DeleteCatalogItemResponse>() .Produces<DeleteCatalogItemResponse>()
.WithTags("CatalogItemEndpoints"); .WithTags("CatalogItemEndpoints");
} }
public async Task<IResult> HandleAsync(DeleteCatalogItemRequest request) public async Task<IResult> HandleAsync(DeleteCatalogItemRequest request, IRepository<CatalogItem> itemRepository)
{ {
var response = new DeleteCatalogItemResponse(request.CorrelationId()); var response = new DeleteCatalogItemResponse(request.CorrelationId());
var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId); var itemToDelete = await itemRepository.GetByIdAsync(request.CatalogItemId);
if (itemToDelete is null) if (itemToDelete is null)
return Results.NotFound(); return Results.NotFound();
await _itemRepository.DeleteAsync(itemToDelete); await itemRepository.DeleteAsync(itemToDelete);
return Results.Ok(response); return Results.Ok(response);
} }

View File

@@ -13,9 +13,8 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
/// <summary> /// <summary>
/// Updates a Catalog Item /// Updates a Catalog Item
/// </summary> /// </summary>
public class UpdateCatalogItemEndpoint : IEndpoint<IResult, UpdateCatalogItemRequest> public class UpdateCatalogItemEndpoint : IEndpoint<IResult, UpdateCatalogItemRequest, IRepository<CatalogItem>>
{ {
private IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer; private readonly IUriComposer _uriComposer;
public UpdateCatalogItemEndpoint(IUriComposer uriComposer) public UpdateCatalogItemEndpoint(IUriComposer uriComposer)
@@ -29,25 +28,24 @@ public class UpdateCatalogItemEndpoint : IEndpoint<IResult, UpdateCatalogItemReq
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] async
(UpdateCatalogItemRequest request, IRepository<CatalogItem> itemRepository) => (UpdateCatalogItemRequest request, IRepository<CatalogItem> itemRepository) =>
{ {
_itemRepository = itemRepository; return await HandleAsync(request, itemRepository);
return await HandleAsync(request);
}) })
.Produces<UpdateCatalogItemResponse>() .Produces<UpdateCatalogItemResponse>()
.WithTags("CatalogItemEndpoints"); .WithTags("CatalogItemEndpoints");
} }
public async Task<IResult> HandleAsync(UpdateCatalogItemRequest request) public async Task<IResult> HandleAsync(UpdateCatalogItemRequest request, IRepository<CatalogItem> itemRepository)
{ {
var response = new UpdateCatalogItemResponse(request.CorrelationId()); var response = new UpdateCatalogItemResponse(request.CorrelationId());
var existingItem = await _itemRepository.GetByIdAsync(request.Id); var existingItem = await itemRepository.GetByIdAsync(request.Id);
CatalogItem.CatalogItemDetails details = new(request.Name, request.Description, request.Price); CatalogItem.CatalogItemDetails details = new(request.Name, request.Description, request.Price);
existingItem.UpdateDetails(details); existingItem.UpdateDetails(details);
existingItem.UpdateBrand(request.CatalogBrandId); existingItem.UpdateBrand(request.CatalogBrandId);
existingItem.UpdateType(request.CatalogTypeId); existingItem.UpdateType(request.CatalogTypeId);
await _itemRepository.UpdateAsync(existingItem); await itemRepository.UpdateAsync(existingItem);
var dto = new CatalogItemDto var dto = new CatalogItemDto
{ {

View File

@@ -13,33 +13,31 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
/// <summary> /// <summary>
/// List Catalog Types /// List Catalog Types
/// </summary> /// </summary>
public class CatalogTypeListEndpoint : IEndpoint<IResult> public class CatalogTypeListEndpoint : IEndpoint<IResult, IRepository<CatalogType>>
{ {
private IRepository<CatalogType> _catalogTypeRepository;
private readonly IMapper _mapper; private readonly IMapper _mapper;
public CatalogTypeListEndpoint(IMapper mapper) public CatalogTypeListEndpoint(IMapper mapper)
{ {
_mapper = mapper; _mapper = mapper;
} }
public void AddRoute(IEndpointRouteBuilder app) public void AddRoute(IEndpointRouteBuilder app)
{ {
app.MapGet("api/catalog-types", app.MapGet("api/catalog-types",
async (IRepository<CatalogType> catalogTypeRepository) => async (IRepository<CatalogType> catalogTypeRepository) =>
{ {
_catalogTypeRepository = catalogTypeRepository; return await HandleAsync(catalogTypeRepository);
return await HandleAsync();
}) })
.Produces<ListCatalogTypesResponse>() .Produces<ListCatalogTypesResponse>()
.WithTags("CatalogTypeEndpoints"); .WithTags("CatalogTypeEndpoints");
} }
public async Task<IResult> HandleAsync() public async Task<IResult> HandleAsync(IRepository<CatalogType> catalogTypeRepository)
{ {
var response = new ListCatalogTypesResponse(); var response = new ListCatalogTypesResponse();
var items = await _catalogTypeRepository.ListAsync(); var items = await catalogTypeRepository.ListAsync();
response.CatalogTypes.AddRange(items.Select(_mapper.Map<CatalogTypeDto>)); response.CatalogTypes.AddRange(items.Select(_mapper.Map<CatalogTypeDto>));

View File

@@ -1,11 +1,11 @@
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
EXPOSE 443 EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /app WORKDIR /app
COPY . . COPY . .
#COPY ["src/PublicApi/PublicApi.csproj", "./PublicApi/"] #COPY ["src/PublicApi/PublicApi.csproj", "./PublicApi/"]

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Text; using System.Text;
using BlazorShared; using BlazorShared;
using BlazorShared.Models; using BlazorShared.Models;
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@@ -83,9 +82,8 @@ builder.Services.AddCors(options =>
}); });
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddMediatR(typeof(CatalogItem).Assembly);
builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly); builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly);
builder.Configuration.AddEnvironmentVariables();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => builder.Services.AddSwaggerGen(c =>

View File

@@ -1,13 +1,4 @@
{ {
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:52023",
"sslPort": 44339
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": { "profiles": {
"IIS Express": { "IIS Express": {
"commandName": "IISExpress", "commandName": "IISExpress",
@@ -25,6 +16,32 @@
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"applicationUrl": "https://localhost:5099;http://localhost:5098" "applicationUrl": "https://localhost:5099;http://localhost:5098"
},
"WSL": {
"commandName": "WSL2",
"launchBrowser": true,
"launchUrl": "https://localhost:5099/swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "https://localhost:5099;http://localhost:5098"
},
"distributionName": ""
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"publishAllPorts": true,
"useSSL": true
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:52023",
"sslPort": 44339
} }
} }
} }

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.PublicApi</RootNamespace> <RootNamespace>Microsoft.eShopWeb.PublicApi</RootNamespace>
<UserSecretsId>5b662463-1efd-4bae-bde4-befe0be3e8ff</UserSecretsId> <UserSecretsId>5b662463-1efd-4bae-bde4-befe0be3e8ff</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
@@ -10,28 +9,26 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.ApiEndpoints" Version="4.0.1" /> <PackageReference Include="Ardalis.ApiEndpoints" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" />
<PackageReference Include="MediatR" Version="10.0.1" /> <PackageReference Include="MinimalApi.Endpoint" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" /> <PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="MinimalApi.Endpoint" Version="1.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore.Annotations" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.4.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Identity.UI" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" >
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.8" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.23.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"dotnet-ef": { "dotnet-ef": {
"version": "6.0.4", "version": "7.0.2",
"commands": [ "commands": [
"dotnet-ef" "dotnet-ef"
] ]

View File

@@ -1,7 +1,4 @@
using System; using System.Security.Claims;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@@ -10,7 +7,6 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Configuration; using Microsoft.eShopWeb.Web.Configuration;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account; namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account;

View File

@@ -1,8 +1,6 @@
using MediatR; using MediatR;
using Microsoft.eShopWeb.Web.Interfaces; using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.Services; using Microsoft.eShopWeb.Web.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.eShopWeb.Web.Configuration; namespace Microsoft.eShopWeb.Web.Configuration;

View File

@@ -1,11 +1,14 @@
using System.Collections.Generic; using System.Security.Claims;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using BlazorShared.Authorization; using BlazorShared.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Configuration;
using Microsoft.Extensions.Caching.Memory;
namespace Microsoft.eShopWeb.Web.Controllers; namespace Microsoft.eShopWeb.Web.Controllers;
@@ -14,10 +17,19 @@ namespace Microsoft.eShopWeb.Web.Controllers;
public class UserController : ControllerBase public class UserController : ControllerBase
{ {
private readonly ITokenClaimsService _tokenClaimsService; private readonly ITokenClaimsService _tokenClaimsService;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<UserController> _logger;
private readonly IMemoryCache _cache;
public UserController(ITokenClaimsService tokenClaimsService) public UserController(ITokenClaimsService tokenClaimsService,
SignInManager<ApplicationUser> signInManager,
ILogger<UserController> logger,
IMemoryCache cache)
{ {
_tokenClaimsService = tokenClaimsService; _tokenClaimsService = tokenClaimsService;
_signInManager = signInManager;
_logger = logger;
_cache = cache;
} }
[HttpGet] [HttpGet]
@@ -26,6 +38,25 @@ public class UserController : ControllerBase
public async Task<IActionResult> GetCurrentUser() => public async Task<IActionResult> GetCurrentUser() =>
Ok(await CreateUserInfo(User)); Ok(await CreateUserInfo(User));
[Route("Logout")]
[HttpPost]
[Authorize]
[AllowAnonymous]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var userId = _signInManager.Context.User.Claims.First(c => c.Type == ClaimTypes.Name);
var identityKey = _signInManager.Context.Request.Cookies[ConfigureCookieSettings.IdentifierCookieName];
_cache.Set($"{userId.Value}:{identityKey}", identityKey, new MemoryCacheEntryOptions
{
AbsoluteExpiration = DateTime.Now.AddMinutes(ConfigureCookieSettings.ValidityMinutesPeriod)
});
_logger.LogInformation("User logged out.");
return Ok();
}
private async Task<UserInfo> CreateUserInfo(ClaimsPrincipal claimsPrincipal) private async Task<UserInfo> CreateUserInfo(ClaimsPrincipal claimsPrincipal)
{ {
if (claimsPrincipal.Identity == null || claimsPrincipal.Identity.Name == null || !claimsPrincipal.Identity.IsAuthenticated) if (claimsPrincipal.Identity == null || claimsPrincipal.Identity.Name == null || !claimsPrincipal.Identity.IsAuthenticated)

View File

@@ -7,7 +7,7 @@
# #
# RUN COMMAND # RUN COMMAND
# docker run --name eshopweb --rm -it -p 5106:5106 web # docker run --name eshopweb --rm -it -p 5106:5106 web
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /app WORKDIR /app
COPY *.sln . COPY *.sln .
@@ -17,7 +17,7 @@ RUN dotnet restore
RUN dotnet publish -c Release -o out RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
WORKDIR /app WORKDIR /app
COPY --from=build /app/src/Web/out ./ COPY --from=build /app/src/Web/out ./

View File

@@ -1,8 +1,4 @@
using System.Collections.Generic; using MediatR;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.ApplicationCore.Specifications;

View File

@@ -1,7 +1,4 @@
using System.Linq; using MediatR;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.ApplicationCore.Specifications;

View File

@@ -53,7 +53,7 @@ builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultTokenProviders(); .AddDefaultTokenProviders();
builder.Services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>(); builder.Services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>();
builder.Configuration.AddEnvironmentVariables();
builder.Services.AddCoreServices(builder.Configuration); builder.Services.AddCoreServices(builder.Configuration);
builder.Services.AddWebServices(builder.Configuration); builder.Services.AddWebServices(builder.Configuration);

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>Microsoft.eShopWeb.Web</RootNamespace> <RootNamespace>Microsoft.eShopWeb.Web</RootNamespace>
@@ -14,28 +13,28 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Ardalis.ListStartupServices" Version="1.1.4" /> <PackageReference Include="Ardalis.ListStartupServices" />
<PackageReference Include="Ardalis.Specification" Version="6.1.0" /> <PackageReference Include="Ardalis.Specification" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" />
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.1" /> <PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" />
<PackageReference Include="Azure.Identity" Version="1.5.0" /> <PackageReference Include="Azure.Identity" />
<PackageReference Include="MediatR" Version="10.0.1" /> <PackageReference Include="MediatR" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" /> <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" />
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" Condition="'$(Configuration)'=='Release'" PrivateAssets="All" /> <PackageReference Include="BuildBundlerMinifier" Condition="'$(Configuration)'=='Release'" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.8" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" /> <PackageReference Include="Microsoft.Web.LibraryManager.Build" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Identity.UI" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.23.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="wwwroot\fonts\" /> <Folder Include="wwwroot\fonts\" />

View File

@@ -3,11 +3,11 @@
"defaultProvider": "cdnjs", "defaultProvider": "cdnjs",
"libraries": [ "libraries": [
{ {
"library": "jquery@3.3.1", "library": "jquery@3.6.3",
"destination": "wwwroot/lib/jquery/" "destination": "wwwroot/lib/jquery/"
}, },
{ {
"library": "twitter-bootstrap@3.3.7", "library": "twitter-bootstrap@5.2.3",
"files": [ "files": [
"css/bootstrap.css", "css/bootstrap.css",
"css/bootstrap.css.map", "css/bootstrap.css.map",
@@ -19,11 +19,11 @@
"destination": "wwwroot/lib/bootstrap/dist/" "destination": "wwwroot/lib/bootstrap/dist/"
}, },
{ {
"library": "jquery-validation-unobtrusive@3.2.10", "library": "jquery-validation-unobtrusive@4.0.0",
"destination": "wwwroot/lib/jquery-validation-unobtrusive/" "destination": "wwwroot/lib/jquery-validation-unobtrusive/"
}, },
{ {
"library": "jquery-validate@1.17.0", "library": "jquery-validate@1.19.5",
"destination": "wwwroot/lib/jquery-validate/", "destination": "wwwroot/lib/jquery-validate/",
"files": [ "files": [
"jquery.validate.min.js", "jquery.validate.min.js",
@@ -35,7 +35,7 @@
"destination": "wwwroot/lib/toastr/" "destination": "wwwroot/lib/toastr/"
}, },
{ {
"library": "aspnet-signalr@1.0.3", "library": "aspnet-signalr@1.0.27",
"files": [ "files": [
"signalr.js", "signalr.js",
"signalr.min.js" "signalr.min.js"
@@ -43,4 +43,4 @@
"destination": "wwwroot/lib/@aspnet/signalr/dist/browser/" "destination": "wwwroot/lib/@aspnet/signalr/dist/browser/"
} }
] ]
} }

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.FunctionalTests</RootNamespace> <RootNamespace>Microsoft.eShopWeb.FunctionalTests</RootNamespace>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
@@ -15,15 +14,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio" />
<PrivateAssets>all</PrivateAssets> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <DotNetCliToolReference Include="dotnet-xunit" />
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,17 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.IntegrationTests</RootNamespace> <RootNamespace>Microsoft.eShopWeb.IntegrationTests</RootNamespace>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" />
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>

View File

@@ -2,7 +2,10 @@
using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
using Microsoft.eShopWeb.Web.ViewModels; using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace PublicApiIntegrationTests.CatalogItemEndpoints namespace PublicApiIntegrationTests.CatalogItemEndpoints
@@ -45,5 +48,26 @@ namespace PublicApiIntegrationTests.CatalogItemEndpoints
Assert.AreEqual(totalExpected, model2.CatalogItems.Count()); Assert.AreEqual(totalExpected, model2.CatalogItems.Count());
} }
[DataTestMethod]
[DataRow("catalog-items")]
[DataRow("catalog-brands")]
[DataRow("catalog-types")]
[DataRow("catalog-items/1")]
public async Task SuccessFullMutipleParallelCall(string endpointName)
{
var client = ProgramTest.NewClient;
var tasks = new List<Task<HttpResponseMessage>>();
for (int i = 0; i < 100; i++)
{
var task = client.GetAsync($"/api/{endpointName}");
tasks.Add(task);
}
await Task.WhenAll(tasks.ToList());
var totalKO = tasks.Count(t => t.Result.StatusCode != HttpStatusCode.OK);
Assert.AreEqual(0, totalKO);
}
} }
} }

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@@ -20,11 +19,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" /> <PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" /> <PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="coverlet.collector" Version="3.1.2" /> <PackageReference Include="coverlet.collector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RootNamespace>Microsoft.eShopWeb.UnitTests</RootNamespace> <RootNamespace>Microsoft.eShopWeb.UnitTests</RootNamespace>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@@ -10,18 +9,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" />
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio" />
<PrivateAssets>all</PrivateAssets> <PackageReference Include="xunit.runner.console" />
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.console" Version="2.4.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>