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:
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user