Shady nagy/net6 (#614)

* udated to .net6

* used the .net6 version RC2

* added editconfig.

* App core new Scoped Namespaces style.

* BlazorAdmin new Scoped Namespaces style.

* Blazor Shared new Scoped Namespaces style.

* Infra new Scoped Namespaces style.

* public api new Scoped Namespaces style.

* web new Scoped Namespaces style.

* FunctionalTests new Scoped Namespaces style.

* Integrational tests new Scoped Namespaces style.

* unit tests new Scoped Namespaces style.

* update github action.

* update github action.

* change the global.
This commit is contained in:
Shady Nagy
2021-11-06 01:55:48 +02:00
committed by GitHub
parent 64f150dc07
commit 9db2feb930
252 changed files with 6307 additions and 6413 deletions

View File

@@ -1,8 +1,7 @@
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
public class AuthenticateRequest : BaseRequest
{
public class AuthenticateRequest : BaseRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
public string Username { get; set; }
public string Password { get; set; }
}

View File

@@ -1,21 +1,20 @@
using System;
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints
{
public class AuthenticateResponse : BaseResponse
{
public AuthenticateResponse(Guid correlationId) : base(correlationId)
{
}
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
public AuthenticateResponse()
{
}
public bool Result { get; set; } = false;
public string Token { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public bool IsLockedOut { get; set; } = false;
public bool IsNotAllowed { get; set; } = false;
public bool RequiresTwoFactor { get; set; } = false;
public class AuthenticateResponse : BaseResponse
{
public AuthenticateResponse(Guid correlationId) : base(correlationId)
{
}
public AuthenticateResponse()
{
}
public bool Result { get; set; } = false;
public string Token { get; set; } = string.Empty;
public string Username { get; set; } = string.Empty;
public bool IsLockedOut { get; set; } = false;
public bool IsNotAllowed { get; set; } = false;
public bool RequiresTwoFactor { get; set; } = false;
}

View File

@@ -3,21 +3,20 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
public class ClaimValue
{
public class ClaimValue
public ClaimValue()
{
public ClaimValue()
{
}
public ClaimValue(string type, string value)
{
Type = type;
Value = value;
}
public string Type { get; set; }
public string Value { get; set; }
}
public ClaimValue(string type, string value)
{
Type = type;
Value = value;
}
public string Type { get; set; }
public string Value { get; set; }
}

View File

@@ -3,14 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
public class UserInfo
{
public class UserInfo
{
public static readonly UserInfo Anonymous = new UserInfo();
public bool IsAuthenticated { get; set; }
public string NameClaimType { get; set; }
public string RoleClaimType { get; set; }
public IEnumerable<ClaimValue> Claims { get; set; }
}
public static readonly UserInfo Anonymous = new UserInfo();
public bool IsAuthenticated { get; set; }
public string NameClaimType { get; set; }
public string RoleClaimType { get; set; }
public IEnumerable<ClaimValue> Claims { get; set; }
}

View File

@@ -1,56 +1,55 @@
using Ardalis.ApiEndpoints;
using System.Threading;
using System.Threading.Tasks;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Swashbuckle.AspNetCore.Annotations;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
public class Authenticate : BaseAsyncEndpoint
.WithRequest<AuthenticateRequest>
.WithResponse<AuthenticateResponse>
{
public class Authenticate : BaseAsyncEndpoint
.WithRequest<AuthenticateRequest>
.WithResponse<AuthenticateResponse>
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ITokenClaimsService _tokenClaimsService;
public Authenticate(SignInManager<ApplicationUser> signInManager,
ITokenClaimsService tokenClaimsService)
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ITokenClaimsService _tokenClaimsService;
_signInManager = signInManager;
_tokenClaimsService = tokenClaimsService;
}
public Authenticate(SignInManager<ApplicationUser> signInManager,
ITokenClaimsService tokenClaimsService)
[HttpPost("api/authenticate")]
[SwaggerOperation(
Summary = "Authenticates a user",
Description = "Authenticates a user",
OperationId = "auth.authenticate",
Tags = new[] { "AuthEndpoints" })
]
public override async Task<ActionResult<AuthenticateResponse>> HandleAsync(AuthenticateRequest request, CancellationToken cancellationToken)
{
var response = new AuthenticateResponse(request.CorrelationId());
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
//var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
var result = await _signInManager.PasswordSignInAsync(request.Username, request.Password, false, true);
response.Result = result.Succeeded;
response.IsLockedOut = result.IsLockedOut;
response.IsNotAllowed = result.IsNotAllowed;
response.RequiresTwoFactor = result.RequiresTwoFactor;
response.Username = request.Username;
if (result.Succeeded)
{
_signInManager = signInManager;
_tokenClaimsService = tokenClaimsService;
response.Token = await _tokenClaimsService.GetTokenAsync(request.Username);
}
[HttpPost("api/authenticate")]
[SwaggerOperation(
Summary = "Authenticates a user",
Description = "Authenticates a user",
OperationId = "auth.authenticate",
Tags = new[] { "AuthEndpoints" })
]
public override async Task<ActionResult<AuthenticateResponse>> HandleAsync(AuthenticateRequest request, CancellationToken cancellationToken)
{
var response = new AuthenticateResponse(request.CorrelationId());
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
//var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
var result = await _signInManager.PasswordSignInAsync(request.Username, request.Password, false, true);
response.Result = result.Succeeded;
response.IsLockedOut = result.IsLockedOut;
response.IsNotAllowed = result.IsNotAllowed;
response.RequiresTwoFactor = result.RequiresTwoFactor;
response.Username = request.Username;
if (result.Succeeded)
{
response.Token = await _tokenClaimsService.GetTokenAsync(request.Username);
}
return response;
}
return response;
}
}

View File

@@ -1,16 +1,15 @@
using System;
namespace Microsoft.eShopWeb.PublicApi
namespace Microsoft.eShopWeb.PublicApi;
/// <summary>
/// Base class used by API requests
/// </summary>
public abstract class BaseMessage
{
/// <summary>
/// Base class used by API requests
/// Unique Identifier used by logging
/// </summary>
public abstract class BaseMessage
{
/// <summary>
/// Unique Identifier used by logging
/// </summary>
protected Guid _correlationId = Guid.NewGuid();
public Guid CorrelationId() => _correlationId;
}
protected Guid _correlationId = Guid.NewGuid();
public Guid CorrelationId() => _correlationId;
}

View File

@@ -1,9 +1,8 @@
namespace Microsoft.eShopWeb.PublicApi
namespace Microsoft.eShopWeb.PublicApi;
/// <summary>
/// Base class used by API requests
/// </summary>
public abstract class BaseRequest : BaseMessage
{
/// <summary>
/// Base class used by API requests
/// </summary>
public abstract class BaseRequest : BaseMessage
{
}
}

View File

@@ -1,19 +1,18 @@
using System;
namespace Microsoft.eShopWeb.PublicApi
{
/// <summary>
/// Base class used by API responses
/// </summary>
public abstract class BaseResponse : BaseMessage
{
public BaseResponse(Guid correlationId) : base()
{
base._correlationId = correlationId;
}
namespace Microsoft.eShopWeb.PublicApi;
public BaseResponse()
{
}
/// <summary>
/// Base class used by API responses
/// </summary>
public abstract class BaseResponse : BaseMessage
{
public BaseResponse(Guid correlationId) : base()
{
base._correlationId = correlationId;
}
public BaseResponse()
{
}
}

View File

@@ -1,8 +1,7 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
public class CatalogBrandDto
{
public class CatalogBrandDto
{
public int Id { get; set; }
public string Name { get; set; }
}
public int Id { get; set; }
public string Name { get; set; }
}

View File

@@ -1,18 +1,17 @@
using System;
using System.Collections.Generic;
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
public class ListCatalogBrandsResponse : BaseResponse
{
public class ListCatalogBrandsResponse : BaseResponse
public ListCatalogBrandsResponse(Guid correlationId) : base(correlationId)
{
public ListCatalogBrandsResponse(Guid correlationId) : base(correlationId)
{
}
public ListCatalogBrandsResponse()
{
}
public List<CatalogBrandDto> CatalogBrands { get; set; } = new List<CatalogBrandDto>();
}
public ListCatalogBrandsResponse()
{
}
public List<CatalogBrandDto> CatalogBrands { get; set; } = new List<CatalogBrandDto>();
}

View File

@@ -1,45 +1,44 @@
using Ardalis.ApiEndpoints;
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;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
public class List : BaseAsyncEndpoint
.WithoutRequest
.WithResponse<ListCatalogBrandsResponse>
{
public class List : BaseAsyncEndpoint
.WithoutRequest
.WithResponse<ListCatalogBrandsResponse>
private readonly IRepository<CatalogBrand> _catalogBrandRepository;
private readonly IMapper _mapper;
public List(IRepository<CatalogBrand> catalogBrandRepository,
IMapper mapper)
{
private readonly IRepository<CatalogBrand> _catalogBrandRepository;
private readonly IMapper _mapper;
_catalogBrandRepository = catalogBrandRepository;
_mapper = mapper;
}
public List(IRepository<CatalogBrand> 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<ActionResult<ListCatalogBrandsResponse>> HandleAsync(CancellationToken cancellationToken)
{
var response = new ListCatalogBrandsResponse();
[HttpGet("api/catalog-brands")]
[SwaggerOperation(
Summary = "List Catalog Brands",
Description = "List Catalog Brands",
OperationId = "catalog-brands.List",
Tags = new[] { "CatalogBrandEndpoints" })
]
public override async Task<ActionResult<ListCatalogBrandsResponse>> HandleAsync(CancellationToken cancellationToken)
{
var response = new ListCatalogBrandsResponse();
var items = await _catalogBrandRepository.ListAsync(cancellationToken);
var items = await _catalogBrandRepository.ListAsync(cancellationToken);
response.CatalogBrands.AddRange(items.Select(_mapper.Map<CatalogBrandDto>));
response.CatalogBrands.AddRange(items.Select(_mapper.Map<CatalogBrandDto>));
return Ok(response);
}
return Ok(response);
}
}

View File

@@ -1,13 +1,12 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class CatalogItemDto
{
public class CatalogItemDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string PictureUri { get; set; }
public int CatalogTypeId { get; set; }
public int CatalogBrandId { get; set; }
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string PictureUri { get; set; }
public int CatalogTypeId { get; set; }
public int CatalogBrandId { get; set; }
}

View File

@@ -1,15 +1,13 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
{
public class CreateCatalogItemRequest : BaseRequest
{
public int CatalogBrandId { get; set; }
public int CatalogTypeId { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public string PictureUri { get; set; }
public string PictureBase64 { get; set; }
public string PictureName { get; set; }
public decimal Price { get; set; }
}
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class CreateCatalogItemRequest : BaseRequest
{
public int CatalogBrandId { get; set; }
public int CatalogTypeId { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public string PictureUri { get; set; }
public string PictureBase64 { get; set; }
public string PictureName { get; set; }
public decimal Price { get; set; }
}

View File

@@ -1,17 +1,16 @@
using System;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class CreateCatalogItemResponse : BaseResponse
{
public class CreateCatalogItemResponse : BaseResponse
public CreateCatalogItemResponse(Guid correlationId) : base(correlationId)
{
public CreateCatalogItemResponse(Guid correlationId) : base(correlationId)
{
}
public CreateCatalogItemResponse()
{
}
public CatalogItemDto CatalogItem { get; set; }
}
public CreateCatalogItemResponse()
{
}
public CatalogItemDto CatalogItem { get; set; }
}

View File

@@ -1,4 +1,6 @@
using Ardalis.ApiEndpoints;
using System.Threading;
using System.Threading.Tasks;
using Ardalis.ApiEndpoints;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -7,72 +9,68 @@ using Microsoft.eShopWeb.ApplicationCore.Exceptions;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Swashbuckle.AspNetCore.Annotations;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Create : BaseAsyncEndpoint
.WithRequest<CreateCatalogItemRequest>
.WithResponse<CreateCatalogItemResponse>
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Create : BaseAsyncEndpoint
.WithRequest<CreateCatalogItemRequest>
.WithResponse<CreateCatalogItemResponse>
public Create(IRepository<CatalogItem> itemRepository,
IUriComposer uriComposer)
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
public Create(IRepository<CatalogItem> itemRepository,
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<ActionResult<CreateCatalogItemResponse>> HandleAsync(CreateCatalogItemRequest request, CancellationToken cancellationToken)
{
var response = new CreateCatalogItemResponse(request.CorrelationId());
var catalogItemNameSpecification = new CatalogItemNameSpecification(request.Name);
var existingCataloogItem = await _itemRepository.CountAsync(catalogItemNameSpecification, cancellationToken);
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);
if (newItem.Id != 0)
{
//We disabled the upload functionality and added a default/placeholder image to this sample due to a potential security risk
// pointed out by the community. More info in this issue: https://github.com/dotnet-architecture/eShopOnWeb/issues/537
// 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);
}
var dto = new CatalogItemDto
{
Id = newItem.Id,
CatalogBrandId = newItem.CatalogBrandId,
CatalogTypeId = newItem.CatalogTypeId,
Description = newItem.Description,
Name = newItem.Name,
PictureUri = _uriComposer.ComposePicUri(newItem.PictureUri),
Price = newItem.Price
};
response.CatalogItem = dto;
return response;
}
_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<ActionResult<CreateCatalogItemResponse>> HandleAsync(CreateCatalogItemRequest request, CancellationToken cancellationToken)
{
var response = new CreateCatalogItemResponse(request.CorrelationId());
var catalogItemNameSpecification = new CatalogItemNameSpecification(request.Name);
var existingCataloogItem = await _itemRepository.CountAsync(catalogItemNameSpecification, cancellationToken);
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);
if (newItem.Id != 0)
{
//We disabled the upload functionality and added a default/placeholder image to this sample due to a potential security risk
// pointed out by the community. More info in this issue: https://github.com/dotnet-architecture/eShopOnWeb/issues/537
// 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);
}
var dto = new CatalogItemDto
{
Id = newItem.Id,
CatalogBrandId = newItem.CatalogBrandId,
CatalogTypeId = newItem.CatalogTypeId,
Description = newItem.Description,
Name = newItem.Name,
PictureUri = _uriComposer.ComposePicUri(newItem.PictureUri),
Price = newItem.Price
};
response.CatalogItem = dto;
return response;
}
}

View File

@@ -1,10 +1,9 @@
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class DeleteCatalogItemRequest : BaseRequest
{
public class DeleteCatalogItemRequest : BaseRequest
{
//[FromRoute]
public int CatalogItemId { get; set; }
}
//[FromRoute]
public int CatalogItemId { get; set; }
}

View File

@@ -1,17 +1,16 @@
using System;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class DeleteCatalogItemResponse : BaseResponse
{
public class DeleteCatalogItemResponse : BaseResponse
public DeleteCatalogItemResponse(Guid correlationId) : base(correlationId)
{
public DeleteCatalogItemResponse(Guid correlationId) : base(correlationId)
{
}
public DeleteCatalogItemResponse()
{
}
public string Status { get; set; } = "Deleted";
}
public DeleteCatalogItemResponse()
{
}
public string Status { get; set; } = "Deleted";
}

View File

@@ -1,44 +1,43 @@
using Ardalis.ApiEndpoints;
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;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Delete : BaseAsyncEndpoint
.WithRequest<DeleteCatalogItemRequest>
.WithResponse<DeleteCatalogItemResponse>
{
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Delete : BaseAsyncEndpoint
.WithRequest<DeleteCatalogItemRequest>
.WithResponse<DeleteCatalogItemResponse>
private readonly IRepository<CatalogItem> _itemRepository;
public Delete(IRepository<CatalogItem> itemRepository)
{
private readonly IRepository<CatalogItem> _itemRepository;
_itemRepository = itemRepository;
}
public Delete(IRepository<CatalogItem> 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<ActionResult<DeleteCatalogItemResponse>> HandleAsync([FromRoute] DeleteCatalogItemRequest request, CancellationToken cancellationToken)
{
var response = new DeleteCatalogItemResponse(request.CorrelationId());
[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<ActionResult<DeleteCatalogItemResponse>> 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();
var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId, cancellationToken);
if (itemToDelete is null) return NotFound();
await _itemRepository.DeleteAsync(itemToDelete, cancellationToken);
await _itemRepository.DeleteAsync(itemToDelete, cancellationToken);
return Ok(response);
}
return Ok(response);
}
}

View File

@@ -1,7 +1,6 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class GetByIdCatalogItemRequest : BaseRequest
{
public class GetByIdCatalogItemRequest : BaseRequest
{
public int CatalogItemId { get; set; }
}
public int CatalogItemId { get; set; }
}

View File

@@ -1,17 +1,16 @@
using System;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class GetByIdCatalogItemResponse : BaseResponse
{
public class GetByIdCatalogItemResponse : BaseResponse
public GetByIdCatalogItemResponse(Guid correlationId) : base(correlationId)
{
public GetByIdCatalogItemResponse(Guid correlationId) : base(correlationId)
{
}
public GetByIdCatalogItemResponse()
{
}
public CatalogItemDto CatalogItem { get; set; }
}
public GetByIdCatalogItemResponse()
{
}
public CatalogItemDto CatalogItem { get; set; }
}

View File

@@ -1,51 +1,50 @@
using Ardalis.ApiEndpoints;
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;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class GetById : BaseAsyncEndpoint
.WithRequest<GetByIdCatalogItemRequest>
.WithResponse<GetByIdCatalogItemResponse>
{
public class GetById : BaseAsyncEndpoint
.WithRequest<GetByIdCatalogItemRequest>
.WithResponse<GetByIdCatalogItemResponse>
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
public GetById(IRepository<CatalogItem> itemRepository, IUriComposer uriComposer)
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
_itemRepository = itemRepository;
_uriComposer = uriComposer;
}
public GetById(IRepository<CatalogItem> itemRepository, IUriComposer 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<ActionResult<GetByIdCatalogItemResponse>> 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
{
_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<ActionResult<GetByIdCatalogItemResponse>> 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);
}
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);
}
}

View File

@@ -1,10 +1,9 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class ListPagedCatalogItemRequest : BaseRequest
{
public class ListPagedCatalogItemRequest : BaseRequest
{
public int PageSize { get; set; }
public int PageIndex { get; set; }
public int? CatalogBrandId { get; set; }
public int? CatalogTypeId { get; set; }
}
public int PageSize { get; set; }
public int PageIndex { get; set; }
public int? CatalogBrandId { get; set; }
public int? CatalogTypeId { get; set; }
}

View File

@@ -1,19 +1,18 @@
using System;
using System.Collections.Generic;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class ListPagedCatalogItemResponse : BaseResponse
{
public class ListPagedCatalogItemResponse : BaseResponse
public ListPagedCatalogItemResponse(Guid correlationId) : base(correlationId)
{
public ListPagedCatalogItemResponse(Guid correlationId) : base(correlationId)
{
}
public ListPagedCatalogItemResponse()
{
}
public List<CatalogItemDto> CatalogItems { get; set; } = new List<CatalogItemDto>();
public int PageCount { get; set; }
}
public ListPagedCatalogItemResponse()
{
}
public List<CatalogItemDto> CatalogItems { get; set; } = new List<CatalogItemDto>();
public int PageCount { get; set; }
}

View File

@@ -1,72 +1,71 @@
using Ardalis.ApiEndpoints;
using System;
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 Microsoft.eShopWeb.ApplicationCore.Specifications;
using Swashbuckle.AspNetCore.Annotations;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class ListPaged : BaseAsyncEndpoint
.WithRequest<ListPagedCatalogItemRequest>
.WithResponse<ListPagedCatalogItemResponse>
{
public class ListPaged : BaseAsyncEndpoint
.WithRequest<ListPagedCatalogItemRequest>
.WithResponse<ListPagedCatalogItemResponse>
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
private readonly IMapper _mapper;
public ListPaged(IRepository<CatalogItem> itemRepository,
IUriComposer uriComposer,
IMapper mapper)
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
private readonly IMapper _mapper;
_itemRepository = itemRepository;
_uriComposer = uriComposer;
_mapper = mapper;
}
public ListPaged(IRepository<CatalogItem> itemRepository,
IUriComposer uriComposer,
IMapper 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<ActionResult<ListPagedCatalogItemResponse>> HandleAsync([FromQuery] ListPagedCatalogItemRequest request, CancellationToken cancellationToken)
{
var response = new ListPagedCatalogItemResponse(request.CorrelationId());
var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId);
int totalItems = await _itemRepository.CountAsync(filterSpec, cancellationToken);
var pagedSpec = new CatalogFilterPaginatedSpecification(
skip: request.PageIndex * request.PageSize,
take: request.PageSize,
brandId: request.CatalogBrandId,
typeId: request.CatalogTypeId);
var items = await _itemRepository.ListAsync(pagedSpec, cancellationToken);
response.CatalogItems.AddRange(items.Select(_mapper.Map<CatalogItemDto>));
foreach (CatalogItemDto item in response.CatalogItems)
{
_itemRepository = itemRepository;
_uriComposer = uriComposer;
_mapper = mapper;
item.PictureUri = _uriComposer.ComposePicUri(item.PictureUri);
}
[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<ActionResult<ListPagedCatalogItemResponse>> HandleAsync([FromQuery] ListPagedCatalogItemRequest request, CancellationToken cancellationToken)
if (request.PageSize > 0)
{
var response = new ListPagedCatalogItemResponse(request.CorrelationId());
var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId);
int totalItems = await _itemRepository.CountAsync(filterSpec, cancellationToken);
var pagedSpec = new CatalogFilterPaginatedSpecification(
skip: request.PageIndex * request.PageSize,
take: request.PageSize,
brandId: request.CatalogBrandId,
typeId: request.CatalogTypeId);
var items = await _itemRepository.ListAsync(pagedSpec, cancellationToken);
response.CatalogItems.AddRange(items.Select(_mapper.Map<CatalogItemDto>));
foreach (CatalogItemDto item in response.CatalogItems)
{
item.PictureUri = _uriComposer.ComposePicUri(item.PictureUri);
}
if (request.PageSize > 0)
{
response.PageCount = int.Parse(Math.Ceiling((decimal)totalItems / request.PageSize).ToString());
}
else
{
response.PageCount = totalItems > 0 ? 1 : 0;
}
return Ok(response);
response.PageCount = int.Parse(Math.Ceiling((decimal)totalItems / request.PageSize).ToString());
}
else
{
response.PageCount = totalItems > 0 ? 1 : 0;
}
return Ok(response);
}
}

View File

@@ -1,23 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class UpdateCatalogItemRequest : BaseRequest
{
public class UpdateCatalogItemRequest : BaseRequest
{
[Range(1, 10000)]
public int Id { get; set; }
[Range(1, 10000)]
public int CatalogBrandId { get; set; }
[Range(1, 10000)]
public int CatalogTypeId { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Name { get; set; }
public string PictureBase64 { get; set; }
public string PictureUri { get; set; }
public string PictureName { get; set; }
[Range(0.01, 10000)]
public decimal Price { get; set; }
}
[Range(1, 10000)]
public int Id { get; set; }
[Range(1, 10000)]
public int CatalogBrandId { get; set; }
[Range(1, 10000)]
public int CatalogTypeId { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Name { get; set; }
public string PictureBase64 { get; set; }
public string PictureUri { get; set; }
public string PictureName { get; set; }
[Range(0.01, 10000)]
public decimal Price { get; set; }
}

View File

@@ -1,17 +1,16 @@
using System;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class UpdateCatalogItemResponse : BaseResponse
{
public class UpdateCatalogItemResponse : BaseResponse
public UpdateCatalogItemResponse(Guid correlationId) : base(correlationId)
{
public UpdateCatalogItemResponse(Guid correlationId) : base(correlationId)
{
}
public UpdateCatalogItemResponse()
{
}
public CatalogItemDto CatalogItem { get; set; }
}
public UpdateCatalogItemResponse()
{
}
public CatalogItemDto CatalogItem { get; set; }
}

View File

@@ -1,61 +1,60 @@
using Ardalis.ApiEndpoints;
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;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Update : BaseAsyncEndpoint
.WithRequest<UpdateCatalogItemRequest>
.WithResponse<UpdateCatalogItemResponse>
{
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Update : BaseAsyncEndpoint
.WithRequest<UpdateCatalogItemRequest>
.WithResponse<UpdateCatalogItemResponse>
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
public Update(IRepository<CatalogItem> itemRepository, IUriComposer uriComposer)
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
_itemRepository = itemRepository;
_uriComposer = uriComposer;
}
public Update(IRepository<CatalogItem> itemRepository, IUriComposer 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<ActionResult<UpdateCatalogItemResponse>> 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
{
_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<ActionResult<UpdateCatalogItemResponse>> 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;
}
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;
}
}

View File

@@ -1,8 +1,7 @@
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
public class CatalogTypeDto
{
public class CatalogTypeDto
{
public int Id { get; set; }
public string Name { get; set; }
}
public int Id { get; set; }
public string Name { get; set; }
}

View File

@@ -1,18 +1,17 @@
using System;
using System.Collections.Generic;
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
public class ListCatalogTypesResponse : BaseResponse
{
public class ListCatalogTypesResponse : BaseResponse
public ListCatalogTypesResponse(Guid correlationId) : base(correlationId)
{
public ListCatalogTypesResponse(Guid correlationId) : base(correlationId)
{
}
public ListCatalogTypesResponse()
{
}
public List<CatalogTypeDto> CatalogTypes { get; set; } = new List<CatalogTypeDto>();
}
public ListCatalogTypesResponse()
{
}
public List<CatalogTypeDto> CatalogTypes { get; set; } = new List<CatalogTypeDto>();
}

View File

@@ -1,45 +1,44 @@
using Ardalis.ApiEndpoints;
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;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
public class List : BaseAsyncEndpoint
.WithoutRequest
.WithResponse<ListCatalogTypesResponse>
{
public class List : BaseAsyncEndpoint
.WithoutRequest
.WithResponse<ListCatalogTypesResponse>
private readonly IRepository<CatalogType> _catalogTypeRepository;
private readonly IMapper _mapper;
public List(IRepository<CatalogType> catalogTypeRepository,
IMapper mapper)
{
private readonly IRepository<CatalogType> _catalogTypeRepository;
private readonly IMapper _mapper;
_catalogTypeRepository = catalogTypeRepository;
_mapper = mapper;
}
public List(IRepository<CatalogType> 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<ActionResult<ListCatalogTypesResponse>> HandleAsync(CancellationToken cancellationToken)
{
var response = new ListCatalogTypesResponse();
[HttpGet("api/catalog-types")]
[SwaggerOperation(
Summary = "List Catalog Types",
Description = "List Catalog Types",
OperationId = "catalog-types.List",
Tags = new[] { "CatalogTypeEndpoints" })
]
public override async Task<ActionResult<ListCatalogTypesResponse>> HandleAsync(CancellationToken cancellationToken)
{
var response = new ListCatalogTypesResponse();
var items = await _catalogTypeRepository.ListAsync(cancellationToken);
var items = await _catalogTypeRepository.ListAsync(cancellationToken);
response.CatalogTypes.AddRange(items.Select(_mapper.Map<CatalogTypeDto>));
response.CatalogTypes.AddRange(items.Select(_mapper.Map<CatalogTypeDto>));
return Ok(response);
}
return Ok(response);
}
}

View File

@@ -1,17 +1,16 @@
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Microsoft.eShopWeb.PublicApi
{
public class CustomSchemaFilters : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var excludeProperties = new[] { "CorrelationId" };
namespace Microsoft.eShopWeb.PublicApi;
foreach (var prop in excludeProperties)
if (schema.Properties.ContainsKey(prop))
schema.Properties.Remove(prop);
}
public class CustomSchemaFilters : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var excludeProperties = new[] { "CorrelationId" };
foreach (var prop in excludeProperties)
if (schema.Properties.ContainsKey(prop))
schema.Properties.Remove(prop);
}
}

View File

@@ -1,25 +1,24 @@
using System;
using System.IO;
namespace Microsoft.eShopWeb.PublicApi
namespace Microsoft.eShopWeb.PublicApi;
public static class ImageValidators
{
public static class ImageValidators
private const int ImageMaximumBytes = 512000;
public static bool IsValidImage(this byte[] postedFile, string fileName)
{
private const int ImageMaximumBytes = 512000;
return postedFile != null && postedFile.Length > 0 && postedFile.Length <= ImageMaximumBytes && IsExtensionValid(fileName);
}
public static bool IsValidImage(this byte[] postedFile, string fileName)
{
return postedFile != null && postedFile.Length > 0 && postedFile.Length <= ImageMaximumBytes && IsExtensionValid(fileName);
}
private static bool IsExtensionValid(string fileName)
{
var extension = Path.GetExtension(fileName);
private static bool IsExtensionValid(string fileName)
{
var extension = Path.GetExtension(fileName);
return string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase);
}
return string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase);
}
}

View File

@@ -4,17 +4,16 @@ using Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
using Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
namespace Microsoft.eShopWeb.PublicApi
namespace Microsoft.eShopWeb.PublicApi;
public class MappingProfile : Profile
{
public class MappingProfile : Profile
public MappingProfile()
{
public MappingProfile()
{
CreateMap<CatalogItem, CatalogItemDto>();
CreateMap<CatalogType, CatalogTypeDto>()
.ForMember(dto => dto.Name, options => options.MapFrom(src => src.Type));
CreateMap<CatalogBrand, CatalogBrandDto>()
.ForMember(dto => dto.Name, options => options.MapFrom(src => src.Brand));
}
CreateMap<CatalogItem, CatalogItemDto>();
CreateMap<CatalogType, CatalogTypeDto>()
.ForMember(dto => dto.Name, options => options.MapFrom(src => src.Type));
CreateMap<CatalogBrand, CatalogBrandDto>()
.ForMember(dto => dto.Name, options => options.MapFrom(src => src.Brand));
}
}

View File

@@ -1,46 +1,45 @@
using BlazorShared.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
using System;
using System;
using System.Net;
using System.Threading.Tasks;
using BlazorShared.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
namespace Microsoft.eShopWeb.PublicApi.MiddleWares
namespace Microsoft.eShopWeb.PublicApi.MiddleWares;
public class ExceptionMiddleware
{
public class ExceptionMiddleware
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next)
{
private readonly RequestDelegate _next;
_next = next;
}
public ExceptionMiddleware(RequestDelegate next)
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
_next = next;
await _next(httpContext);
}
public async Task InvokeAsync(HttpContext httpContext)
catch (Exception ex)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
await HandleExceptionAsync(httpContext, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
if (exception is DuplicateException duplicationException)
{
context.Response.ContentType = "application/json";
if (exception is DuplicateException duplicationException)
context.Response.StatusCode = (int)HttpStatusCode.Conflict;
await context.Response.WriteAsync(new ErrorDetails()
{
context.Response.StatusCode = (int)HttpStatusCode.Conflict;
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = duplicationException.Message
}.ToString());
}
StatusCode = context.Response.StatusCode,
Message = duplicationException.Message
}.ToString());
}
}
}

View File

@@ -1,3 +1,5 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.eShopWeb.Infrastructure.Data;
@@ -5,47 +7,44 @@ using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.PublicApi
namespace Microsoft.eShopWeb.PublicApi;
public class Program
{
public class Program
public static async Task Main(string[] args)
{
public static async Task Main(string[] args)
var host = CreateHostBuilder(args)
.Build();
using (var scope = host.Services.CreateScope())
{
var host = CreateHostBuilder(args)
.Build();
using (var scope = host.Services.CreateScope())
var services = scope.ServiceProvider;
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
try
{
var services = scope.ServiceProvider;
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
try
{
var catalogContext = services.GetRequiredService<CatalogContext>();
await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory);
var catalogContext = services.GetRequiredService<CatalogContext>();
await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory);
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager);
}
catch (Exception ex)
{
var logger = loggerFactory.CreateLogger<Program>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager);
}
catch (Exception ex)
{
var logger = loggerFactory.CreateLogger<Program>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.PublicApi</RootNamespace>
<UserSecretsId>5b662463-1efd-4bae-bde4-befe0be3e8ff</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Text;
using AutoMapper;
using BlazorShared;
using MediatR;
@@ -20,138 +22,136 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopWeb.PublicApi
namespace Microsoft.eShopWeb.PublicApi;
public class Startup
{
public class Startup
private const string CORS_POLICY = "CorsPolicy";
public Startup(IConfiguration configuration)
{
private const string CORS_POLICY = "CorsPolicy";
Configuration = configuration;
}
public Startup(IConfiguration configuration)
public IConfiguration Configuration { get; }
public void ConfigureDevelopmentServices(IServiceCollection services)
{
// use in-memory database
ConfigureInMemoryDatabases(services);
// use real database
//ConfigureProductionServices(services);
}
public void ConfigureDockerServices(IServiceCollection services)
{
ConfigureDevelopmentServices(services);
}
private void ConfigureInMemoryDatabases(IServiceCollection services)
{
services.AddDbContext<CatalogContext>(c =>
c.UseInMemoryDatabase("Catalog"));
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseInMemoryDatabase("Identity"));
ConfigureServices(services);
}
public void ConfigureProductionServices(IServiceCollection services)
{
// 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<CatalogContext>(c =>
c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection")));
// Add Identity DbContext
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
ConfigureServices(services);
}
public void ConfigureTestingServices(IServiceCollection services)
{
ConfigureInMemoryDatabases(services);
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
services.Configure<CatalogSettings>(Configuration);
services.AddSingleton<IUriComposer>(new UriComposer(Configuration.Get<CatalogSettings>()));
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>();
var baseUrlConfig = new BaseUrlConfiguration();
Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
services.AddMemoryCache();
var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY);
services.AddAuthentication(config =>
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureDevelopmentServices(IServiceCollection services)
config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(config =>
{
// use in-memory database
ConfigureInMemoryDatabases(services);
// use real database
//ConfigureProductionServices(services);
}
public void ConfigureDockerServices(IServiceCollection services)
{
ConfigureDevelopmentServices(services);
}
private void ConfigureInMemoryDatabases(IServiceCollection services)
{
services.AddDbContext<CatalogContext>(c =>
c.UseInMemoryDatabase("Catalog"));
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseInMemoryDatabase("Identity"));
ConfigureServices(services);
}
public void ConfigureProductionServices(IServiceCollection services)
{
// 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<CatalogContext>(c =>
c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection")));
// Add Identity DbContext
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
ConfigureServices(services);
}
public void ConfigureTestingServices(IServiceCollection services)
{
ConfigureInMemoryDatabases(services);
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
services.Configure<CatalogSettings>(Configuration);
services.AddSingleton<IUriComposer>(new UriComposer(Configuration.Get<CatalogSettings>()));
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>();
var baseUrlConfig = new BaseUrlConfiguration();
Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
services.AddMemoryCache();
var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY);
services.AddAuthentication(config =>
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters
{
config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(config =>
{
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddCors(options =>
{
options.AddPolicy(name: CORS_POLICY,
builder =>
{
builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/'));
builder.AllowAnyMethod();
builder.AllowAnyHeader();
});
});
services.AddCors(options =>
{
options.AddPolicy(name: CORS_POLICY,
builder =>
{
builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/'));
builder.AllowAnyMethod();
builder.AllowAnyHeader();
});
});
services.AddControllers();
services.AddMediatR(typeof(CatalogItem).Assembly);
services.AddControllers();
services.AddMediatR(typeof(CatalogItem).Assembly);
services.AddAutoMapper(typeof(Startup).Assembly);
services.AddSwaggerGen(c =>
services.AddAutoMapper(typeof(Startup).Assembly);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
c.EnableAnnotations();
c.SchemaFilter<CustomSchemaFilters>();
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
c.EnableAnnotations();
c.SchemaFilter<CustomSchemaFilters>();
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
Enter 'Bearer' [space] and then your token in the text input below.
\r\n\r\nExample: 'Bearer 12345abcdef'",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
@@ -167,42 +167,41 @@ namespace Microsoft.eShopWeb.PublicApi
},
new List<string>()
}
});
});
}
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<ExceptionMiddleware>();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(CORS_POLICY);
app.UseAuthorization();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<ExceptionMiddleware>();
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(CORS_POLICY);
app.UseAuthorization();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}