From 9db2feb930c8cd1ce379bbebf76521a4ba0dddfb Mon Sep 17 00:00:00 2001 From: Shady Nagy Date: Sat, 6 Nov 2021 01:55:48 +0200 Subject: [PATCH] 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. --- .editorconfig | 134 ++++ .github/workflows/dotnetcore.yml | 3 +- global.json | 6 + src/ApplicationCore/ApplicationCore.csproj | 2 +- src/ApplicationCore/CatalogSettings.cs | 9 +- .../Constants/AuthorizationConstants.cs | 17 +- src/ApplicationCore/Entities/BaseEntity.cs | 13 +- .../Entities/BasketAggregate/Basket.cs | 55 +- .../Entities/BasketAggregate/BasketItem.cs | 45 +- .../Entities/BuyerAggregate/Buyer.cs | 33 +- .../Entities/BuyerAggregate/PaymentMethod.cs | 13 +- src/ApplicationCore/Entities/CatalogBrand.cs | 13 +- src/ApplicationCore/Entities/CatalogItem.cs | 107 ++- src/ApplicationCore/Entities/CatalogType.cs | 13 +- .../Entities/OrderAggregate/Address.cs | 33 +- .../OrderAggregate/CatalogItemOrdered.cs | 45 +- .../Entities/OrderAggregate/Order.cs | 75 +- .../Entities/OrderAggregate/OrderItem.cs | 31 +- .../Exceptions/BasketNotFoundException.cs | 9 +- .../Exceptions/DuplicateException.cs | 14 +- .../EmptyBasketOnCheckoutException.cs | 29 +- .../Extensions/GuardExtensions.cs | 31 +- .../Extensions/JsonExtensions.cs | 21 +- .../Interfaces/IAggregateRoot.cs | 9 +- src/ApplicationCore/Interfaces/IAppLogger.cs | 19 +- .../Interfaces/IBasketQueryService.cs | 9 +- .../Interfaces/IBasketService.cs | 19 +- .../Interfaces/IEmailSender.cs | 10 +- .../Interfaces/IOrderService.cs | 13 +- .../Interfaces/IReadRepository.cs | 7 +- src/ApplicationCore/Interfaces/IRepository.cs | 7 +- .../Interfaces/ITokenClaimsService.cs | 9 +- .../Interfaces/IUriComposer.cs | 9 +- src/ApplicationCore/Services/BasketService.cs | 127 ++- src/ApplicationCore/Services/OrderService.cs | 75 +- src/ApplicationCore/Services/UriComposer.cs | 17 +- .../BasketWithItemsSpecification.cs | 27 +- .../CatalogFilterPaginatedSpecification.cs | 23 +- .../CatalogFilterSpecification.cs | 13 +- .../CatalogItemNameSpecification.cs | 11 +- .../CatalogItemsSpecification.cs | 17 +- .../CustomerOrdersWithItemsSpecification.cs | 15 +- .../Specifications/OrderWithItemsByIdSpec.cs | 17 +- src/BlazorAdmin/BlazorAdmin.csproj | 2 +- src/BlazorAdmin/CustomAuthStateProvider.cs | 119 ++- src/BlazorAdmin/Helpers/BlazorComponent.cs | 35 +- .../Helpers/BlazorLayoutComponent.cs | 33 +- src/BlazorAdmin/Helpers/RefreshBroadcast.cs | 31 +- src/BlazorAdmin/Helpers/ToastComponent.cs | 159 ++-- src/BlazorAdmin/JavaScript/Cookies.cs | 35 +- src/BlazorAdmin/JavaScript/Css.cs | 35 +- .../JavaScript/JSInteropConstants.cs | 17 +- src/BlazorAdmin/JavaScript/Route.cs | 27 +- .../Pages/CatalogItemPage/List.razor.cs | 99 ++- src/BlazorAdmin/Program.cs | 61 +- src/BlazorAdmin/Services/CacheEntry.cs | 23 +- .../CachedCatalogItemServiceDecorator.cs | 173 ++-- ...achedCatalogLookupDataServiceDecorator .cs | 79 +- .../Services/CatalogItemService.cs | 155 ++-- .../Services/CatalogLookupDataService.cs | 59 +- src/BlazorAdmin/Services/HttpService.cs | 135 ++-- src/BlazorAdmin/Services/ToastService.cs | 79 +- src/BlazorAdmin/ServicesConfiguration.cs | 23 +- src/BlazorAdmin/Shared/CustomInputSelect.cs | 49 +- .../Attributes/EndpointAttribute.cs | 9 +- src/BlazorShared/Authorization/ClaimValue.cs | 25 +- src/BlazorShared/Authorization/Constants.cs | 11 +- src/BlazorShared/Authorization/UserInfo.cs | 19 +- src/BlazorShared/BaseUrlConfiguration.cs | 13 +- src/BlazorShared/BlazorShared.csproj | 2 +- .../Interfaces/ICatalogItemService.cs | 19 +- .../Interfaces/ICatalogLookupDataService.cs | 13 +- .../Interfaces/ILookupDataResponse.cs | 13 +- src/BlazorShared/Models/CatalogBrand.cs | 9 +- .../Models/CatalogBrandResponse.cs | 15 +- src/BlazorShared/Models/CatalogItem.cs | 117 ++- src/BlazorShared/Models/CatalogType.cs | 9 +- .../Models/CatalogTypeResponse.cs | 15 +- .../Models/CreateCatalogItemRequest.cs | 35 +- .../Models/CreateCatalogItemResponse.cs | 9 +- .../Models/DeleteCatalogItemResponse.cs | 9 +- .../Models/EditCatalogItemResponse.cs | 9 +- src/BlazorShared/Models/ErrorDetails.cs | 15 +- src/BlazorShared/Models/LookupData.cs | 11 +- .../Models/PagedCatalogItemResponse.cs | 11 +- src/Infrastructure/Data/BasketQueryService.cs | 37 +- src/Infrastructure/Data/CatalogContext.cs | 38 +- src/Infrastructure/Data/CatalogContextSeed.cs | 105 ++- .../Data/Config/BasketConfiguration.cs | 19 +- .../Data/Config/BasketItemConfiguration.cs | 15 +- .../Data/Config/CatalogBrandConfiguration.cs | 23 +- .../Data/Config/CatalogItemConfiguration.cs | 45 +- .../Data/Config/CatalogTypeConfiguration.cs | 23 +- .../Data/Config/OrderConfiguration.cs | 69 +- .../Data/Config/OrderItemConfiguration.cs | 27 +- src/Infrastructure/Data/EfRepository.cs | 11 +- src/Infrastructure/Data/FileItem.cs | 21 +- .../Migrations/20201202111507_InitialModel.cs | 399 +++++----- .../Migrations/20211026175614_FixBuyerId.cs | 87 +-- .../Identity/AppIdentityDbContext.cs | 26 +- .../Identity/AppIdentityDbContextSeed.cs | 29 +- .../Identity/ApplicationUser.cs | 7 +- .../Identity/IdentityTokenClaimService.cs | 61 +- .../20201202111612_InitialIdentityModel.cs | 423 +++++----- src/Infrastructure/Infrastructure.csproj | 2 +- src/Infrastructure/Logging/LoggerAdapter.cs | 29 +- src/Infrastructure/Services/EmailSender.cs | 21 +- .../Authenticate.AuthenticateRequest.cs | 11 +- .../Authenticate.AuthenticateResponse.cs | 27 +- .../AuthEndpoints/Authenticate.ClaimValue.cs | 25 +- .../AuthEndpoints/Authenticate.UserInfo.cs | 17 +- src/PublicApi/AuthEndpoints/Authenticate.cs | 81 +- src/PublicApi/BaseMessage.cs | 19 +- src/PublicApi/BaseRequest.cs | 13 +- src/PublicApi/BaseResponse.cs | 23 +- .../CatalogBrandEndpoints/CatalogBrandDto.cs | 11 +- .../List.ListCatalogBrandsResponse.cs | 19 +- src/PublicApi/CatalogBrandEndpoints/List.cs | 61 +- .../CatalogItemEndpoints/CatalogItemDto.cs | 21 +- .../Create.CreateCatalogItemRequest.cs | 24 +- .../Create.CreateCatalogItemResponse.cs | 19 +- src/PublicApi/CatalogItemEndpoints/Create.cs | 110 ++- .../Delete.DeleteCatalogItemRequest.cs | 11 +- .../Delete.DeleteCatalogItemResponse.cs | 19 +- src/PublicApi/CatalogItemEndpoints/Delete.cs | 57 +- .../GetById.GetByIdCatalogItemRequest.cs | 9 +- .../GetById.GetByIdCatalogItemResponse.cs | 19 +- src/PublicApi/CatalogItemEndpoints/GetById.cs | 77 +- .../ListPaged.ListPagedCatalogItemRequest.cs | 15 +- .../ListPaged.ListPagedCatalogItemResponse.cs | 21 +- .../CatalogItemEndpoints/ListPaged.cs | 107 ++- .../Update.UpdateCatalogItemRequest.cs | 37 +- .../Update.UpdateCatalogItemResponse.cs | 19 +- src/PublicApi/CatalogItemEndpoints/Update.cs | 95 ++- .../CatalogTypeEndpoints/CatalogTypeDto.cs | 11 +- .../List.ListCatalogTypesResponse.cs | 19 +- src/PublicApi/CatalogTypeEndpoints/List.cs | 61 +- src/PublicApi/CustomSchemaFilters.cs | 17 +- src/PublicApi/ImageValidators.cs | 31 +- src/PublicApi/MappingProfile.cs | 19 +- .../MiddleWares/ExceptionMiddleware.cs | 59 +- src/PublicApi/Program.cs | 65 +- src/PublicApi/PublicApi.csproj | 2 +- src/PublicApi/Startup.cs | 265 ++++--- .../Areas/Identity/IdentityHostingStartup.cs | 14 +- .../Pages/Account/ConfirmEmail.cshtml.cs | 47 +- .../Identity/Pages/Account/Login.cshtml.cs | 169 ++-- .../Identity/Pages/Account/Logout.cshtml.cs | 75 +- .../Identity/Pages/Account/Register.cshtml.cs | 135 ++-- .../Configuration/ConfigureCookieSettings.cs | 49 +- .../Configuration/ConfigureCoreServices.cs | 29 +- src/Web/Configuration/ConfigureWebServices.cs | 23 +- .../RevokeAuthenticationEvents.cs | 47 +- src/Web/Constants.cs | 15 +- src/Web/Controllers/Api/BaseApiController.cs | 15 +- src/Web/Controllers/ManageController.cs | 737 +++++++++--------- src/Web/Controllers/OrderController.cs | 55 +- src/Web/Controllers/UserController.cs | 107 ++- src/Web/Extensions/CacheHelpers.cs | 33 +- src/Web/Extensions/EmailSenderExtensions.cs | 17 +- src/Web/Extensions/UrlHelperExtensions.cs | 19 +- src/Web/Features/MyOrders/GetMyOrders.cs | 19 +- .../Features/MyOrders/GetMyOrdersHandler.cs | 63 +- .../Features/OrderDetails/GetOrderDetails.cs | 19 +- .../OrderDetails/GetOrderDetailsHandler.cs | 67 +- src/Web/HealthChecks/ApiHealthCheck.cs | 57 +- src/Web/HealthChecks/HomePageHealthCheck.cs | 49 +- src/Web/Interfaces/IBasketViewModelService.cs | 17 +- .../ICatalogItemViewModelService.cs | 13 +- .../Interfaces/ICatalogViewModelService.cs | 19 +- src/Web/Pages/Admin/EditCatalogItem.cshtml.cs | 45 +- src/Web/Pages/Admin/Index.cshtml.cs | 17 +- src/Web/Pages/Basket/BasketItemViewModel.cs | 25 +- src/Web/Pages/Basket/BasketViewModel.cs | 19 +- src/Web/Pages/Basket/Checkout.cshtml.cs | 141 ++-- src/Web/Pages/Basket/Index.cshtml.cs | 121 ++- src/Web/Pages/Basket/Success.cshtml.cs | 13 +- src/Web/Pages/Error.cshtml.cs | 23 +- src/Web/Pages/Index.cshtml.cs | 29 +- src/Web/Pages/Privacy.cshtml.cs | 9 +- .../Components/BasketComponent/Basket.cs | 77 +- src/Web/Program.cs | 65 +- src/Web/Services/BasketViewModelService.cs | 141 ++-- .../Services/CachedCatalogViewModelService.cs | 73 +- .../Services/CatalogItemViewModelService.cs | 31 +- src/Web/Services/CatalogViewModelService.cs | 181 +++-- src/Web/SlugifyParameterTransformer.cs | 20 +- src/Web/Startup.cs | 336 ++++---- src/Web/ViewModels/Account/LoginViewModel.cs | 23 +- .../Account/LoginWith2faViewModel.cs | 23 +- .../ViewModels/Account/RegisterViewModel.cs | 33 +- .../Account/ResetPasswordViewModel.cs | 31 +- .../ViewModels/BasketComponentViewModel.cs | 9 +- src/Web/ViewModels/CatalogIndexViewModel.cs | 23 +- src/Web/ViewModels/CatalogItemViewModel.cs | 15 +- src/Web/ViewModels/File/FileViewModel.cs | 13 +- .../Manage/ChangePasswordViewModel.cs | 35 +- .../Manage/EnableAuthenticatorViewModel.cs | 23 +- .../Manage/ExternalLoginsViewModel.cs | 19 +- .../Manage/GenerateRecoveryCodesViewModel.cs | 9 +- src/Web/ViewModels/Manage/IndexViewModel.cs | 25 +- .../ViewModels/Manage/RemoveLoginViewModel.cs | 11 +- .../ViewModels/Manage/SetPasswordViewModel.cs | 27 +- .../TwoFactorAuthenticationViewModel.cs | 13 +- src/Web/ViewModels/OrderItemViewModel.cs | 19 +- src/Web/ViewModels/OrderViewModel.cs | 25 +- src/Web/ViewModels/PaginationInfoViewModel.cs | 19 +- src/Web/Views/Manage/ManageNavPages.cs | 41 +- src/Web/Web.csproj | 2 +- tests/FunctionalTests/FunctionalTests.csproj | 2 +- .../PublicApi/ApiTestFixture.cs | 77 +- .../PublicApi/ApiTokenHelper.cs | 69 +- .../AuthEndpoints/AuthenticateEndpoint.cs | 61 +- .../ApiCatalogControllerList.cs | 57 +- .../CatalogItemEndpoints/CreateEndpoint.cs | 111 ++- .../CatalogItemEndpoints/DeleteEndpoint.cs | 75 +- .../CatalogItemEndpoints/GetByIdEndpoint.cs | 67 +- .../Controllers/AccountControllerSignIn.cs | 129 ++- .../Web/Controllers/CatalogControllerIndex.cs | 35 +- .../Web/Controllers/OrderControllerIndex.cs | 39 +- .../Web/Pages/BasketPageCheckout.cs | 93 ++- .../Web/Pages/HomePageOnGet.cs | 39 +- tests/FunctionalTests/Web/WebTestFixture.cs | 77 +- .../IntegrationTests/IntegrationTests.csproj | 2 +- .../BasketRepositoryTests/SetQuantities.cs | 53 +- .../OrderRepositoryTests/GetById.cs | 65 +- .../GetByIdWithItemsAsync.cs | 105 ++- .../Entities/BasketTests/BasketAddItem.cs | 135 ++-- .../BasketTests/BasketRemoveEmptyItems.cs | 27 +- .../CatalogItemTests/UpdateDetails.cs | 99 ++- .../Entities/OrderTests/OrderTotal.cs | 53 +- .../Extensions/JsonExtensions.cs | 45 +- .../ApplicationCore/Extensions/TestChild.cs | 17 +- .../ApplicationCore/Extensions/TestParent.cs | 23 +- .../BasketServiceTests/AddItemToBasket.cs | 53 +- .../BasketServiceTests/DeleteBasket.cs | 37 +- .../BasketServiceTests/SetQuantities.cs | 43 +- .../BasketServiceTests/TransferBasket.cs | 159 ++-- .../BasketWithItemsSpecification.cs | 99 ++- .../CatalogFilterPaginatedSpecification.cs | 81 +- .../CatalogFilterSpecification.cs | 49 +- .../CatalogItemsSpecification.cs | 75 +- .../CustomerOrdersWithItemsSpecification.cs | 95 ++- tests/UnitTests/Builders/AddressBuilder.cs | 43 +- tests/UnitTests/Builders/BasketBuilder.cs | 65 +- tests/UnitTests/Builders/OrderBuilder.cs | 77 +- .../OrdersTests/GetMyOrders.cs | 47 +- .../OrdersTests/GetOrderDetails.cs | 49 +- tests/UnitTests/UnitTests.csproj | 2 +- .../GenerateBrandsCacheKey.cs | 15 +- .../GenerateCatalogItemCacheKey.cs | 21 +- .../GenerateTypesCacheKey.cs | 15 +- 252 files changed, 6120 insertions(+), 6226 deletions(-) create mode 100644 .editorconfig create mode 100644 global.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..9261202f8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,134 @@ +############################### +# Core EditorConfig Options # +############################### +root = true +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +# Namespaces +csharp_style_namespace_declarations = file_scoped:warning +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion \ No newline at end of file diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index e59a440b8..4fcc6d4d2 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -12,7 +12,8 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: '5.0.x' + dotnet-version: '6.0.x' + include-prerelease: true - name: Build with dotnet run: dotnet build ./eShopOnWeb.sln --configuration Release diff --git a/global.json b/global.json new file mode 100644 index 000000000..957199cf2 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "6.0.x", + "rollForward": "latestFeature" + } +} diff --git a/src/ApplicationCore/ApplicationCore.csproj b/src/ApplicationCore/ApplicationCore.csproj index 15462bfd5..de37dc0be 100644 --- a/src/ApplicationCore/ApplicationCore.csproj +++ b/src/ApplicationCore/ApplicationCore.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Microsoft.eShopWeb.ApplicationCore diff --git a/src/ApplicationCore/CatalogSettings.cs b/src/ApplicationCore/CatalogSettings.cs index 39a5880d8..5ea8bd2ad 100644 --- a/src/ApplicationCore/CatalogSettings.cs +++ b/src/ApplicationCore/CatalogSettings.cs @@ -1,7 +1,6 @@ -namespace Microsoft.eShopWeb +namespace Microsoft.eShopWeb; + +public class CatalogSettings { - public class CatalogSettings - { - public string CatalogBaseUrl { get; set; } - } + public string CatalogBaseUrl { get; set; } } diff --git a/src/ApplicationCore/Constants/AuthorizationConstants.cs b/src/ApplicationCore/Constants/AuthorizationConstants.cs index 0235217a3..9503420ef 100644 --- a/src/ApplicationCore/Constants/AuthorizationConstants.cs +++ b/src/ApplicationCore/Constants/AuthorizationConstants.cs @@ -1,13 +1,12 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Constants +namespace Microsoft.eShopWeb.ApplicationCore.Constants; + +public class AuthorizationConstants { - public class AuthorizationConstants - { - public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes"; + public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes"; - // TODO: Don't use this in production - public const string DEFAULT_PASSWORD = "Pass@word1"; + // TODO: Don't use this in production + public const string DEFAULT_PASSWORD = "Pass@word1"; - // TODO: Change this to an environment variable - public const string JWT_SECRET_KEY = "SecretKeyOfDoomThatMustBeAMinimumNumberOfBytes"; - } + // TODO: Change this to an environment variable + public const string JWT_SECRET_KEY = "SecretKeyOfDoomThatMustBeAMinimumNumberOfBytes"; } diff --git a/src/ApplicationCore/Entities/BaseEntity.cs b/src/ApplicationCore/Entities/BaseEntity.cs index 596e7c16b..0bec6d7a1 100644 --- a/src/ApplicationCore/Entities/BaseEntity.cs +++ b/src/ApplicationCore/Entities/BaseEntity.cs @@ -1,9 +1,8 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Entities +namespace Microsoft.eShopWeb.ApplicationCore.Entities; + +// This can easily be modified to be BaseEntity and public T Id to support different key types. +// Using non-generic integer types for simplicity and to ease caching logic +public abstract class BaseEntity { - // This can easily be modified to be BaseEntity and public T Id to support different key types. - // Using non-generic integer types for simplicity and to ease caching logic - public abstract class BaseEntity - { - public virtual int Id { get; protected set; } - } + public virtual int Id { get; protected set; } } diff --git a/src/ApplicationCore/Entities/BasketAggregate/Basket.cs b/src/ApplicationCore/Entities/BasketAggregate/Basket.cs index be07bfe6c..3ac086755 100644 --- a/src/ApplicationCore/Entities/BasketAggregate/Basket.cs +++ b/src/ApplicationCore/Entities/BasketAggregate/Basket.cs @@ -1,39 +1,38 @@ -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate +namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; + +public class Basket : BaseEntity, IAggregateRoot { - public class Basket : BaseEntity, IAggregateRoot - { - public string BuyerId { get; private set; } - private readonly List _items = new List(); - public IReadOnlyCollection Items => _items.AsReadOnly(); + public string BuyerId { get; private set; } + private readonly List _items = new List(); + public IReadOnlyCollection Items => _items.AsReadOnly(); - public Basket(string buyerId) - { - BuyerId = buyerId; - } + public Basket(string buyerId) + { + BuyerId = buyerId; + } - public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1) + public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1) + { + if (!Items.Any(i => i.CatalogItemId == catalogItemId)) { - if (!Items.Any(i => i.CatalogItemId == catalogItemId)) - { - _items.Add(new BasketItem(catalogItemId, quantity, unitPrice)); - return; - } - var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId); - existingItem.AddQuantity(quantity); + _items.Add(new BasketItem(catalogItemId, quantity, unitPrice)); + return; } + var existingItem = Items.FirstOrDefault(i => i.CatalogItemId == catalogItemId); + existingItem.AddQuantity(quantity); + } - public void RemoveEmptyItems() - { - _items.RemoveAll(i => i.Quantity == 0); - } + public void RemoveEmptyItems() + { + _items.RemoveAll(i => i.Quantity == 0); + } - public void SetNewBuyerId(string buyerId) - { - BuyerId = buyerId; - } + public void SetNewBuyerId(string buyerId) + { + BuyerId = buyerId; } } diff --git a/src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs b/src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs index 10fb1c909..5c4c252c8 100644 --- a/src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs +++ b/src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs @@ -1,34 +1,33 @@ using Ardalis.GuardClauses; -namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate +namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; + +public class BasketItem : BaseEntity { - public class BasketItem : BaseEntity - { - public decimal UnitPrice { get; private set; } - public int Quantity { get; private set; } - public int CatalogItemId { get; private set; } - public int BasketId { get; private set; } + public decimal UnitPrice { get; private set; } + public int Quantity { get; private set; } + public int CatalogItemId { get; private set; } + public int BasketId { get; private set; } - public BasketItem(int catalogItemId, int quantity, decimal unitPrice) - { - CatalogItemId = catalogItemId; - UnitPrice = unitPrice; - SetQuantity(quantity); - } + public BasketItem(int catalogItemId, int quantity, decimal unitPrice) + { + CatalogItemId = catalogItemId; + UnitPrice = unitPrice; + SetQuantity(quantity); + } - public void AddQuantity(int quantity) - { - Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue); + public void AddQuantity(int quantity) + { + Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue); - Quantity += quantity; - } + Quantity += quantity; + } - public void SetQuantity(int quantity) - { - Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue); + public void SetQuantity(int quantity) + { + Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue); - Quantity = quantity; - } + Quantity = quantity; } } diff --git a/src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs b/src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs index 915ac1b72..72f7f1a8f 100644 --- a/src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs +++ b/src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs @@ -1,26 +1,25 @@ -using Ardalis.GuardClauses; +using System.Collections.Generic; +using Ardalis.GuardClauses; using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System.Collections.Generic; -namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate +namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate; + +public class Buyer : BaseEntity, IAggregateRoot { - public class Buyer : BaseEntity, IAggregateRoot - { - public string IdentityGuid { get; private set; } + public string IdentityGuid { get; private set; } - private List _paymentMethods = new List(); + private List _paymentMethods = new List(); - public IEnumerable PaymentMethods => _paymentMethods.AsReadOnly(); + public IEnumerable PaymentMethods => _paymentMethods.AsReadOnly(); - private Buyer() - { - // required by EF - } + private Buyer() + { + // required by EF + } - public Buyer(string identity) : this() - { - Guard.Against.NullOrEmpty(identity, nameof(identity)); - IdentityGuid = identity; - } + public Buyer(string identity) : this() + { + Guard.Against.NullOrEmpty(identity, nameof(identity)); + IdentityGuid = identity; } } diff --git a/src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs b/src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs index 9bd193856..f4d2e5420 100644 --- a/src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs +++ b/src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs @@ -1,9 +1,8 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate +namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate; + +public class PaymentMethod : BaseEntity { - public class PaymentMethod : BaseEntity - { - public string Alias { get; private set; } - public string CardId { get; private set; } // actual card data must be stored in a PCI compliant system, like Stripe - public string Last4 { get; private set; } - } + public string Alias { get; private set; } + public string CardId { get; private set; } // actual card data must be stored in a PCI compliant system, like Stripe + public string Last4 { get; private set; } } diff --git a/src/ApplicationCore/Entities/CatalogBrand.cs b/src/ApplicationCore/Entities/CatalogBrand.cs index 44dc7d0a4..868ef53c8 100644 --- a/src/ApplicationCore/Entities/CatalogBrand.cs +++ b/src/ApplicationCore/Entities/CatalogBrand.cs @@ -1,13 +1,12 @@ using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.ApplicationCore.Entities +namespace Microsoft.eShopWeb.ApplicationCore.Entities; + +public class CatalogBrand : BaseEntity, IAggregateRoot { - public class CatalogBrand : BaseEntity, IAggregateRoot + public string Brand { get; private set; } + public CatalogBrand(string brand) { - public string Brand { get; private set; } - public CatalogBrand(string brand) - { - Brand = brand; - } + Brand = brand; } } diff --git a/src/ApplicationCore/Entities/CatalogItem.cs b/src/ApplicationCore/Entities/CatalogItem.cs index 6239815c8..abf5a0260 100644 --- a/src/ApplicationCore/Entities/CatalogItem.cs +++ b/src/ApplicationCore/Entities/CatalogItem.cs @@ -1,66 +1,65 @@ -using Ardalis.GuardClauses; +using System; +using Ardalis.GuardClauses; using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System; -namespace Microsoft.eShopWeb.ApplicationCore.Entities +namespace Microsoft.eShopWeb.ApplicationCore.Entities; + +public class CatalogItem : BaseEntity, IAggregateRoot { - public class CatalogItem : BaseEntity, IAggregateRoot - { - public string Name { get; private set; } - public string Description { get; private set; } - public decimal Price { get; private set; } - public string PictureUri { get; private set; } - public int CatalogTypeId { get; private set; } - public CatalogType CatalogType { get; private set; } - public int CatalogBrandId { get; private set; } - public CatalogBrand CatalogBrand { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + public decimal Price { get; private set; } + public string PictureUri { get; private set; } + public int CatalogTypeId { get; private set; } + public CatalogType CatalogType { get; private set; } + public int CatalogBrandId { get; private set; } + public CatalogBrand CatalogBrand { get; private set; } - public CatalogItem(int catalogTypeId, - int catalogBrandId, - string description, - string name, - decimal price, - string pictureUri) - { - CatalogTypeId = catalogTypeId; - CatalogBrandId = catalogBrandId; - Description = description; - Name = name; - Price = price; - PictureUri = pictureUri; - } + public CatalogItem(int catalogTypeId, + int catalogBrandId, + string description, + string name, + decimal price, + string pictureUri) + { + CatalogTypeId = catalogTypeId; + CatalogBrandId = catalogBrandId; + Description = description; + Name = name; + Price = price; + PictureUri = pictureUri; + } - public void UpdateDetails(string name, string description, decimal price) - { - Guard.Against.NullOrEmpty(name, nameof(name)); - Guard.Against.NullOrEmpty(description, nameof(description)); - Guard.Against.NegativeOrZero(price, nameof(price)); + public void UpdateDetails(string name, string description, decimal price) + { + Guard.Against.NullOrEmpty(name, nameof(name)); + Guard.Against.NullOrEmpty(description, nameof(description)); + Guard.Against.NegativeOrZero(price, nameof(price)); - Name = name; - Description = description; - Price = price; - } + Name = name; + Description = description; + Price = price; + } - public void UpdateBrand(int catalogBrandId) - { - Guard.Against.Zero(catalogBrandId, nameof(catalogBrandId)); - CatalogBrandId = catalogBrandId; - } + public void UpdateBrand(int catalogBrandId) + { + Guard.Against.Zero(catalogBrandId, nameof(catalogBrandId)); + CatalogBrandId = catalogBrandId; + } - public void UpdateType(int catalogTypeId) - { - Guard.Against.Zero(catalogTypeId, nameof(catalogTypeId)); - CatalogTypeId = catalogTypeId; - } + public void UpdateType(int catalogTypeId) + { + Guard.Against.Zero(catalogTypeId, nameof(catalogTypeId)); + CatalogTypeId = catalogTypeId; + } - public void UpdatePictureUri(string pictureName) + public void UpdatePictureUri(string pictureName) + { + if (string.IsNullOrEmpty(pictureName)) { - if (string.IsNullOrEmpty(pictureName)) - { - PictureUri = string.Empty; - return; - } - PictureUri = $"images\\products\\{pictureName}?{new DateTime().Ticks}"; + PictureUri = string.Empty; + return; } + PictureUri = $"images\\products\\{pictureName}?{new DateTime().Ticks}"; } -} \ No newline at end of file +} diff --git a/src/ApplicationCore/Entities/CatalogType.cs b/src/ApplicationCore/Entities/CatalogType.cs index f0e7f8c79..3a355b4eb 100644 --- a/src/ApplicationCore/Entities/CatalogType.cs +++ b/src/ApplicationCore/Entities/CatalogType.cs @@ -1,13 +1,12 @@ using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.ApplicationCore.Entities +namespace Microsoft.eShopWeb.ApplicationCore.Entities; + +public class CatalogType : BaseEntity, IAggregateRoot { - public class CatalogType : BaseEntity, IAggregateRoot + public string Type { get; private set; } + public CatalogType(string type) { - public string Type { get; private set; } - public CatalogType(string type) - { - Type = type; - } + Type = type; } } diff --git a/src/ApplicationCore/Entities/OrderAggregate/Address.cs b/src/ApplicationCore/Entities/OrderAggregate/Address.cs index eaab8f7e4..65bd26136 100644 --- a/src/ApplicationCore/Entities/OrderAggregate/Address.cs +++ b/src/ApplicationCore/Entities/OrderAggregate/Address.cs @@ -1,26 +1,25 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate +namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; + +public class Address // ValueObject { - public class Address // ValueObject - { - public string Street { get; private set; } + public string Street { get; private set; } - public string City { get; private set; } + public string City { get; private set; } - public string State { get; private set; } + public string State { get; private set; } - public string Country { get; private set; } + public string Country { get; private set; } - public string ZipCode { get; private set; } + public string ZipCode { get; private set; } - private Address() { } + private Address() { } - public Address(string street, string city, string state, string country, string zipcode) - { - Street = street; - City = city; - State = state; - Country = country; - ZipCode = zipcode; - } + public Address(string street, string city, string state, string country, string zipcode) + { + Street = street; + City = city; + State = state; + Country = country; + ZipCode = zipcode; } } diff --git a/src/ApplicationCore/Entities/OrderAggregate/CatalogItemOrdered.cs b/src/ApplicationCore/Entities/OrderAggregate/CatalogItemOrdered.cs index 8daed9ffe..98cce2291 100644 --- a/src/ApplicationCore/Entities/OrderAggregate/CatalogItemOrdered.cs +++ b/src/ApplicationCore/Entities/OrderAggregate/CatalogItemOrdered.cs @@ -1,31 +1,30 @@ using Ardalis.GuardClauses; -namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate +namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; + +/// +/// Represents a snapshot of the item that was ordered. If catalog item details change, details of +/// the item that was part of a completed order should not change. +/// +public class CatalogItemOrdered // ValueObject { - /// - /// Represents a snapshot of the item that was ordered. If catalog item details change, details of - /// the item that was part of a completed order should not change. - /// - public class CatalogItemOrdered // ValueObject + public CatalogItemOrdered(int catalogItemId, string productName, string pictureUri) { - public CatalogItemOrdered(int catalogItemId, string productName, string pictureUri) - { - Guard.Against.OutOfRange(catalogItemId, nameof(catalogItemId), 1, int.MaxValue); - Guard.Against.NullOrEmpty(productName, nameof(productName)); - Guard.Against.NullOrEmpty(pictureUri, nameof(pictureUri)); - - CatalogItemId = catalogItemId; - ProductName = productName; - PictureUri = pictureUri; - } + Guard.Against.OutOfRange(catalogItemId, nameof(catalogItemId), 1, int.MaxValue); + Guard.Against.NullOrEmpty(productName, nameof(productName)); + Guard.Against.NullOrEmpty(pictureUri, nameof(pictureUri)); - private CatalogItemOrdered() - { - // required by EF - } + CatalogItemId = catalogItemId; + ProductName = productName; + PictureUri = pictureUri; + } - public int CatalogItemId { get; private set; } - public string ProductName { get; private set; } - public string PictureUri { get; private set; } + private CatalogItemOrdered() + { + // required by EF } + + public int CatalogItemId { get; private set; } + public string ProductName { get; private set; } + public string PictureUri { get; private set; } } diff --git a/src/ApplicationCore/Entities/OrderAggregate/Order.cs b/src/ApplicationCore/Entities/OrderAggregate/Order.cs index fab8b0e5b..53c587e6f 100644 --- a/src/ApplicationCore/Entities/OrderAggregate/Order.cs +++ b/src/ApplicationCore/Entities/OrderAggregate/Order.cs @@ -1,52 +1,51 @@ -using Ardalis.GuardClauses; -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System; +using System; using System.Collections.Generic; +using Ardalis.GuardClauses; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; + +namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate +public class Order : BaseEntity, IAggregateRoot { - public class Order : BaseEntity, IAggregateRoot + private Order() { - private Order() - { - // required by EF - } + // required by EF + } - public Order(string buyerId, Address shipToAddress, List items) - { - Guard.Against.NullOrEmpty(buyerId, nameof(buyerId)); - Guard.Against.Null(shipToAddress, nameof(shipToAddress)); - Guard.Against.Null(items, nameof(items)); + public Order(string buyerId, Address shipToAddress, List items) + { + Guard.Against.NullOrEmpty(buyerId, nameof(buyerId)); + Guard.Against.Null(shipToAddress, nameof(shipToAddress)); + Guard.Against.Null(items, nameof(items)); - BuyerId = buyerId; - ShipToAddress = shipToAddress; - _orderItems = items; - } + BuyerId = buyerId; + ShipToAddress = shipToAddress; + _orderItems = items; + } - public string BuyerId { get; private set; } - public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now; - public Address ShipToAddress { get; private set; } + public string BuyerId { get; private set; } + public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now; + public Address ShipToAddress { get; private set; } - // DDD Patterns comment - // Using a private collection field, better for DDD Aggregate's encapsulation - // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, - // but only through the method Order.AddOrderItem() which includes behavior. - private readonly List _orderItems = new List(); + // DDD Patterns comment + // Using a private collection field, better for DDD Aggregate's encapsulation + // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, + // but only through the method Order.AddOrderItem() which includes behavior. + private readonly List _orderItems = new List(); - // Using List<>.AsReadOnly() - // This will create a read only wrapper around the private list so is protected against "external updates". - // It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance) - //https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx - public IReadOnlyCollection OrderItems => _orderItems.AsReadOnly(); + // Using List<>.AsReadOnly() + // This will create a read only wrapper around the private list so is protected against "external updates". + // It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance) + //https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx + public IReadOnlyCollection OrderItems => _orderItems.AsReadOnly(); - public decimal Total() + public decimal Total() + { + var total = 0m; + foreach (var item in _orderItems) { - var total = 0m; - foreach (var item in _orderItems) - { - total += item.UnitPrice * item.Units; - } - return total; + total += item.UnitPrice * item.Units; } + return total; } } diff --git a/src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs b/src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs index b115df820..fec258258 100644 --- a/src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs +++ b/src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs @@ -1,21 +1,20 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate +namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; + +public class OrderItem : BaseEntity { - public class OrderItem : BaseEntity - { - public CatalogItemOrdered ItemOrdered { get; private set; } - public decimal UnitPrice { get; private set; } - public int Units { get; private set; } + public CatalogItemOrdered ItemOrdered { get; private set; } + public decimal UnitPrice { get; private set; } + public int Units { get; private set; } - private OrderItem() - { - // required by EF - } + private OrderItem() + { + // required by EF + } - public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units) - { - ItemOrdered = itemOrdered; - UnitPrice = unitPrice; - Units = units; - } + public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units) + { + ItemOrdered = itemOrdered; + UnitPrice = unitPrice; + Units = units; } } diff --git a/src/ApplicationCore/Exceptions/BasketNotFoundException.cs b/src/ApplicationCore/Exceptions/BasketNotFoundException.cs index b3a20bd98..7d833034f 100644 --- a/src/ApplicationCore/Exceptions/BasketNotFoundException.cs +++ b/src/ApplicationCore/Exceptions/BasketNotFoundException.cs @@ -1,11 +1,10 @@ using System; -namespace Microsoft.eShopWeb.ApplicationCore.Exceptions +namespace Microsoft.eShopWeb.ApplicationCore.Exceptions; + +public class BasketNotFoundException : Exception { - public class BasketNotFoundException : Exception + public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}") { - public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}") - { - } } } diff --git a/src/ApplicationCore/Exceptions/DuplicateException.cs b/src/ApplicationCore/Exceptions/DuplicateException.cs index 96bd24bb9..152daa5ff 100644 --- a/src/ApplicationCore/Exceptions/DuplicateException.cs +++ b/src/ApplicationCore/Exceptions/DuplicateException.cs @@ -1,14 +1,12 @@ using System; -namespace Microsoft.eShopWeb.ApplicationCore.Exceptions -{ +namespace Microsoft.eShopWeb.ApplicationCore.Exceptions; - public class DuplicateException : Exception +public class DuplicateException : Exception +{ + public DuplicateException(string message) : base(message) { - public DuplicateException(string message) : base(message) - { - - } - + } + } diff --git a/src/ApplicationCore/Exceptions/EmptyBasketOnCheckoutException.cs b/src/ApplicationCore/Exceptions/EmptyBasketOnCheckoutException.cs index c2713a98a..45e9ecccb 100644 --- a/src/ApplicationCore/Exceptions/EmptyBasketOnCheckoutException.cs +++ b/src/ApplicationCore/Exceptions/EmptyBasketOnCheckoutException.cs @@ -1,24 +1,23 @@ using System; -namespace Microsoft.eShopWeb.ApplicationCore.Exceptions +namespace Microsoft.eShopWeb.ApplicationCore.Exceptions; + +public class EmptyBasketOnCheckoutException : Exception { - public class EmptyBasketOnCheckoutException : Exception + public EmptyBasketOnCheckoutException() + : base($"Basket cannot have 0 items on checkout") { - public EmptyBasketOnCheckoutException() - : base($"Basket cannot have 0 items on checkout") - { - } + } - protected EmptyBasketOnCheckoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) - { - } + protected EmptyBasketOnCheckoutException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) + { + } - public EmptyBasketOnCheckoutException(string message) : base(message) - { - } + public EmptyBasketOnCheckoutException(string message) : base(message) + { + } - public EmptyBasketOnCheckoutException(string message, Exception innerException) : base(message, innerException) - { - } + public EmptyBasketOnCheckoutException(string message, Exception innerException) : base(message, innerException) + { } } diff --git a/src/ApplicationCore/Extensions/GuardExtensions.cs b/src/ApplicationCore/Extensions/GuardExtensions.cs index 67796ac9e..06a781aff 100644 --- a/src/ApplicationCore/Extensions/GuardExtensions.cs +++ b/src/ApplicationCore/Extensions/GuardExtensions.cs @@ -1,22 +1,21 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; -using Microsoft.eShopWeb.ApplicationCore.Exceptions; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; +using Microsoft.eShopWeb.ApplicationCore.Exceptions; + +namespace Ardalis.GuardClauses; -namespace Ardalis.GuardClauses +public static class BasketGuards { - public static class BasketGuards + public static void NullBasket(this IGuardClause guardClause, int basketId, Basket basket) { - public static void NullBasket(this IGuardClause guardClause, int basketId, Basket basket) - { - if (basket == null) - throw new BasketNotFoundException(basketId); - } + if (basket == null) + throw new BasketNotFoundException(basketId); + } - public static void EmptyBasketOnCheckout(this IGuardClause guardClause, IReadOnlyCollection basketItems) - { - if (!basketItems.Any()) - throw new EmptyBasketOnCheckoutException(); - } + public static void EmptyBasketOnCheckout(this IGuardClause guardClause, IReadOnlyCollection basketItems) + { + if (!basketItems.Any()) + throw new EmptyBasketOnCheckoutException(); } -} \ No newline at end of file +} diff --git a/src/ApplicationCore/Extensions/JsonExtensions.cs b/src/ApplicationCore/Extensions/JsonExtensions.cs index ec3d61b7c..1a2b8e071 100644 --- a/src/ApplicationCore/Extensions/JsonExtensions.cs +++ b/src/ApplicationCore/Extensions/JsonExtensions.cs @@ -1,18 +1,17 @@ using System.Text.Json; -namespace Microsoft.eShopWeb +namespace Microsoft.eShopWeb; + +public static class JsonExtensions { - public static class JsonExtensions + private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { - private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }; + PropertyNameCaseInsensitive = true + }; - public static T FromJson(this string json) => - JsonSerializer.Deserialize(json, _jsonOptions); + public static T FromJson(this string json) => + JsonSerializer.Deserialize(json, _jsonOptions); - public static string ToJson(this T obj) => - JsonSerializer.Serialize(obj, _jsonOptions); - } + public static string ToJson(this T obj) => + JsonSerializer.Serialize(obj, _jsonOptions); } diff --git a/src/ApplicationCore/Interfaces/IAggregateRoot.cs b/src/ApplicationCore/Interfaces/IAggregateRoot.cs index 5776e9094..9e4911417 100644 --- a/src/ApplicationCore/Interfaces/IAggregateRoot.cs +++ b/src/ApplicationCore/Interfaces/IAggregateRoot.cs @@ -1,5 +1,4 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces -{ - public interface IAggregateRoot - { } -} +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface IAggregateRoot +{ } diff --git a/src/ApplicationCore/Interfaces/IAppLogger.cs b/src/ApplicationCore/Interfaces/IAppLogger.cs index 578637f47..71b3351e5 100644 --- a/src/ApplicationCore/Interfaces/IAppLogger.cs +++ b/src/ApplicationCore/Interfaces/IAppLogger.cs @@ -1,12 +1,11 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +/// +/// This type eliminates the need to depend directly on the ASP.NET Core logging types. +/// +/// +public interface IAppLogger { - /// - /// This type eliminates the need to depend directly on the ASP.NET Core logging types. - /// - /// - public interface IAppLogger - { - void LogInformation(string message, params object[] args); - void LogWarning(string message, params object[] args); - } + void LogInformation(string message, params object[] args); + void LogWarning(string message, params object[] args); } diff --git a/src/ApplicationCore/Interfaces/IBasketQueryService.cs b/src/ApplicationCore/Interfaces/IBasketQueryService.cs index 325a99810..1d1babb2a 100644 --- a/src/ApplicationCore/Interfaces/IBasketQueryService.cs +++ b/src/ApplicationCore/Interfaces/IBasketQueryService.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface IBasketQueryService { - public interface IBasketQueryService - { - Task CountTotalBasketItems(string username); - } + Task CountTotalBasketItems(string username); } diff --git a/src/ApplicationCore/Interfaces/IBasketService.cs b/src/ApplicationCore/Interfaces/IBasketService.cs index 2dd34a3fa..4dbdf9fc9 100644 --- a/src/ApplicationCore/Interfaces/IBasketService.cs +++ b/src/ApplicationCore/Interfaces/IBasketService.cs @@ -1,14 +1,13 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface IBasketService { - public interface IBasketService - { - Task TransferBasketAsync(string anonymousId, string userName); - Task AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1); - Task SetQuantities(int basketId, Dictionary quantities); - Task DeleteBasketAsync(int basketId); - } + Task TransferBasketAsync(string anonymousId, string userName); + Task AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1); + Task SetQuantities(int basketId, Dictionary quantities); + Task DeleteBasketAsync(int basketId); } diff --git a/src/ApplicationCore/Interfaces/IEmailSender.cs b/src/ApplicationCore/Interfaces/IEmailSender.cs index 722f8e447..28c8cdad8 100644 --- a/src/ApplicationCore/Interfaces/IEmailSender.cs +++ b/src/ApplicationCore/Interfaces/IEmailSender.cs @@ -1,10 +1,8 @@ using System.Threading.Tasks; -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces -{ +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; - public interface IEmailSender - { - Task SendEmailAsync(string email, string subject, string message); - } +public interface IEmailSender +{ + Task SendEmailAsync(string email, string subject, string message); } diff --git a/src/ApplicationCore/Interfaces/IOrderService.cs b/src/ApplicationCore/Interfaces/IOrderService.cs index feb0cd508..4adad855c 100644 --- a/src/ApplicationCore/Interfaces/IOrderService.cs +++ b/src/ApplicationCore/Interfaces/IOrderService.cs @@ -1,10 +1,9 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface IOrderService { - public interface IOrderService - { - Task CreateOrderAsync(int basketId, Address shippingAddress); - } + Task CreateOrderAsync(int basketId, Address shippingAddress); } diff --git a/src/ApplicationCore/Interfaces/IReadRepository.cs b/src/ApplicationCore/Interfaces/IReadRepository.cs index 81ec491ce..51b474a76 100644 --- a/src/ApplicationCore/Interfaces/IReadRepository.cs +++ b/src/ApplicationCore/Interfaces/IReadRepository.cs @@ -1,8 +1,7 @@ using Ardalis.Specification; -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface IReadRepository : IReadRepositoryBase where T : class, IAggregateRoot { - public interface IReadRepository : IReadRepositoryBase where T : class, IAggregateRoot - { - } } diff --git a/src/ApplicationCore/Interfaces/IRepository.cs b/src/ApplicationCore/Interfaces/IRepository.cs index c42739601..6904b5a17 100644 --- a/src/ApplicationCore/Interfaces/IRepository.cs +++ b/src/ApplicationCore/Interfaces/IRepository.cs @@ -1,8 +1,7 @@ using Ardalis.Specification; -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface IRepository : IRepositoryBase where T : class, IAggregateRoot { - public interface IRepository : IRepositoryBase where T : class, IAggregateRoot - { - } } diff --git a/src/ApplicationCore/Interfaces/ITokenClaimsService.cs b/src/ApplicationCore/Interfaces/ITokenClaimsService.cs index a378c1cab..af20c535e 100644 --- a/src/ApplicationCore/Interfaces/ITokenClaimsService.cs +++ b/src/ApplicationCore/Interfaces/ITokenClaimsService.cs @@ -1,9 +1,8 @@ using System.Threading.Tasks; -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface ITokenClaimsService { - public interface ITokenClaimsService - { - Task GetTokenAsync(string userName); - } + Task GetTokenAsync(string userName); } diff --git a/src/ApplicationCore/Interfaces/IUriComposer.cs b/src/ApplicationCore/Interfaces/IUriComposer.cs index ee2263c52..943c71456 100644 --- a/src/ApplicationCore/Interfaces/IUriComposer.cs +++ b/src/ApplicationCore/Interfaces/IUriComposer.cs @@ -1,7 +1,6 @@ -namespace Microsoft.eShopWeb.ApplicationCore.Interfaces +namespace Microsoft.eShopWeb.ApplicationCore.Interfaces; + +public interface IUriComposer { - public interface IUriComposer - { - string ComposePicUri(string uriTemplate); - } + string ComposePicUri(string uriTemplate); } diff --git a/src/ApplicationCore/Services/BasketService.cs b/src/ApplicationCore/Services/BasketService.cs index 2fdd9ab4e..167c1cb74 100644 --- a/src/ApplicationCore/Services/BasketService.cs +++ b/src/ApplicationCore/Services/BasketService.cs @@ -1,87 +1,86 @@ -using Ardalis.GuardClauses; +using System.Collections.Generic; +using System.Threading.Tasks; +using Ardalis.GuardClauses; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; -using System.Collections.Generic; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.ApplicationCore.Services +namespace Microsoft.eShopWeb.ApplicationCore.Services; + +public class BasketService : IBasketService { - public class BasketService : IBasketService + private readonly IRepository _basketRepository; + private readonly IAppLogger _logger; + + public BasketService(IRepository basketRepository, + IAppLogger logger) { - private readonly IRepository _basketRepository; - private readonly IAppLogger _logger; + _basketRepository = basketRepository; + _logger = logger; + } - public BasketService(IRepository basketRepository, - IAppLogger logger) - { - _basketRepository = basketRepository; - _logger = logger; - } + public async Task AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1) + { + var basketSpec = new BasketWithItemsSpecification(username); + var basket = await _basketRepository.GetBySpecAsync(basketSpec); - public async Task AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1) + if (basket == null) { - var basketSpec = new BasketWithItemsSpecification(username); - var basket = await _basketRepository.GetBySpecAsync(basketSpec); + basket = new Basket(username); + await _basketRepository.AddAsync(basket); + } - if (basket == null) - { - basket = new Basket(username); - await _basketRepository.AddAsync(basket); - } + basket.AddItem(catalogItemId, price, quantity); - basket.AddItem(catalogItemId, price, quantity); + await _basketRepository.UpdateAsync(basket); + return basket; + } - await _basketRepository.UpdateAsync(basket); - return basket; - } + public async Task DeleteBasketAsync(int basketId) + { + var basket = await _basketRepository.GetByIdAsync(basketId); + await _basketRepository.DeleteAsync(basket); + } - public async Task DeleteBasketAsync(int basketId) - { - var basket = await _basketRepository.GetByIdAsync(basketId); - await _basketRepository.DeleteAsync(basket); - } + public async Task SetQuantities(int basketId, Dictionary quantities) + { + Guard.Against.Null(quantities, nameof(quantities)); + var basketSpec = new BasketWithItemsSpecification(basketId); + var basket = await _basketRepository.GetBySpecAsync(basketSpec); + Guard.Against.NullBasket(basketId, basket); - public async Task SetQuantities(int basketId, Dictionary quantities) + foreach (var item in basket.Items) { - Guard.Against.Null(quantities, nameof(quantities)); - var basketSpec = new BasketWithItemsSpecification(basketId); - var basket = await _basketRepository.GetBySpecAsync(basketSpec); - Guard.Against.NullBasket(basketId, basket); - - foreach (var item in basket.Items) + if (quantities.TryGetValue(item.Id.ToString(), out var quantity)) { - if (quantities.TryGetValue(item.Id.ToString(), out var quantity)) - { - if (_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}."); - item.SetQuantity(quantity); - } + if (_logger != null) _logger.LogInformation($"Updating quantity of item ID:{item.Id} to {quantity}."); + item.SetQuantity(quantity); } - basket.RemoveEmptyItems(); - await _basketRepository.UpdateAsync(basket); - return basket; } + basket.RemoveEmptyItems(); + await _basketRepository.UpdateAsync(basket); + return basket; + } - public async Task TransferBasketAsync(string anonymousId, string userName) + public async Task TransferBasketAsync(string anonymousId, string userName) + { + Guard.Against.NullOrEmpty(anonymousId, nameof(anonymousId)); + Guard.Against.NullOrEmpty(userName, nameof(userName)); + var anonymousBasketSpec = new BasketWithItemsSpecification(anonymousId); + var anonymousBasket = await _basketRepository.GetBySpecAsync(anonymousBasketSpec); + if (anonymousBasket == null) return; + var userBasketSpec = new BasketWithItemsSpecification(userName); + var userBasket = await _basketRepository.GetBySpecAsync(userBasketSpec); + if (userBasket == null) { - Guard.Against.NullOrEmpty(anonymousId, nameof(anonymousId)); - Guard.Against.NullOrEmpty(userName, nameof(userName)); - var anonymousBasketSpec = new BasketWithItemsSpecification(anonymousId); - var anonymousBasket = await _basketRepository.GetBySpecAsync(anonymousBasketSpec); - if (anonymousBasket == null) return; - var userBasketSpec = new BasketWithItemsSpecification(userName); - var userBasket = await _basketRepository.GetBySpecAsync(userBasketSpec); - if (userBasket == null) - { - userBasket = new Basket(userName); - await _basketRepository.AddAsync(userBasket); - } - foreach (var item in anonymousBasket.Items) - { - userBasket.AddItem(item.CatalogItemId, item.UnitPrice, item.Quantity); - } - await _basketRepository.UpdateAsync(userBasket); - await _basketRepository.DeleteAsync(anonymousBasket); + userBasket = new Basket(userName); + await _basketRepository.AddAsync(userBasket); + } + foreach (var item in anonymousBasket.Items) + { + userBasket.AddItem(item.CatalogItemId, item.UnitPrice, item.Quantity); } + await _basketRepository.UpdateAsync(userBasket); + await _basketRepository.DeleteAsync(anonymousBasket); } } diff --git a/src/ApplicationCore/Services/OrderService.cs b/src/ApplicationCore/Services/OrderService.cs index d4119b1d2..1f3852348 100644 --- a/src/ApplicationCore/Services/OrderService.cs +++ b/src/ApplicationCore/Services/OrderService.cs @@ -1,54 +1,53 @@ -using Ardalis.GuardClauses; +using System.Linq; +using System.Threading.Tasks; +using Ardalis.GuardClauses; using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; -using System.Linq; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.ApplicationCore.Services +namespace Microsoft.eShopWeb.ApplicationCore.Services; + +public class OrderService : IOrderService { - public class OrderService : IOrderService + private readonly IRepository _orderRepository; + private readonly IUriComposer _uriComposer; + private readonly IRepository _basketRepository; + private readonly IRepository _itemRepository; + + public OrderService(IRepository basketRepository, + IRepository itemRepository, + IRepository orderRepository, + IUriComposer uriComposer) { - private readonly IRepository _orderRepository; - private readonly IUriComposer _uriComposer; - private readonly IRepository _basketRepository; - private readonly IRepository _itemRepository; - - public OrderService(IRepository basketRepository, - IRepository itemRepository, - IRepository orderRepository, - IUriComposer uriComposer) - { - _orderRepository = orderRepository; - _uriComposer = uriComposer; - _basketRepository = basketRepository; - _itemRepository = itemRepository; - } + _orderRepository = orderRepository; + _uriComposer = uriComposer; + _basketRepository = basketRepository; + _itemRepository = itemRepository; + } - public async Task CreateOrderAsync(int basketId, Address shippingAddress) - { - var basketSpec = new BasketWithItemsSpecification(basketId); - var basket = await _basketRepository.GetBySpecAsync(basketSpec); + public async Task CreateOrderAsync(int basketId, Address shippingAddress) + { + var basketSpec = new BasketWithItemsSpecification(basketId); + var basket = await _basketRepository.GetBySpecAsync(basketSpec); - Guard.Against.NullBasket(basketId, basket); - Guard.Against.EmptyBasketOnCheckout(basket.Items); + Guard.Against.NullBasket(basketId, basket); + Guard.Against.EmptyBasketOnCheckout(basket.Items); - var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray()); - var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification); + var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray()); + var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification); - var items = basket.Items.Select(basketItem => - { - var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId); - var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri)); - var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity); - return orderItem; - }).ToList(); + var items = basket.Items.Select(basketItem => + { + var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId); + var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri)); + var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity); + return orderItem; + }).ToList(); - var order = new Order(basket.BuyerId, shippingAddress, items); + var order = new Order(basket.BuyerId, shippingAddress, items); - await _orderRepository.AddAsync(order); - } + await _orderRepository.AddAsync(order); } } diff --git a/src/ApplicationCore/Services/UriComposer.cs b/src/ApplicationCore/Services/UriComposer.cs index a0f7609c9..0410c8f5f 100644 --- a/src/ApplicationCore/Services/UriComposer.cs +++ b/src/ApplicationCore/Services/UriComposer.cs @@ -1,16 +1,15 @@ using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.ApplicationCore.Services +namespace Microsoft.eShopWeb.ApplicationCore.Services; + +public class UriComposer : IUriComposer { - public class UriComposer : IUriComposer - { - private readonly CatalogSettings _catalogSettings; + private readonly CatalogSettings _catalogSettings; - public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings; + public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings; - public string ComposePicUri(string uriTemplate) - { - return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl); - } + public string ComposePicUri(string uriTemplate) + { + return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl); } } diff --git a/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs b/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs index 457e25feb..015ca31de 100644 --- a/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs +++ b/src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs @@ -1,22 +1,21 @@ using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; -namespace Microsoft.eShopWeb.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.ApplicationCore.Specifications; + +public sealed class BasketWithItemsSpecification : Specification, ISingleResultSpecification { - public sealed class BasketWithItemsSpecification : Specification, ISingleResultSpecification + public BasketWithItemsSpecification(int basketId) { - public BasketWithItemsSpecification(int basketId) - { - Query - .Where(b => b.Id == basketId) - .Include(b => b.Items); - } + Query + .Where(b => b.Id == basketId) + .Include(b => b.Items); + } - public BasketWithItemsSpecification(string buyerId) - { - Query - .Where(b => b.BuyerId == buyerId) - .Include(b => b.Items); - } + public BasketWithItemsSpecification(string buyerId) + { + Query + .Where(b => b.BuyerId == buyerId) + .Include(b => b.Items); } } diff --git a/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs b/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs index abc3e0431..ab8ca57dc 100644 --- a/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs +++ b/src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs @@ -1,21 +1,20 @@ using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities; -namespace Microsoft.eShopWeb.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.ApplicationCore.Specifications; + +public class CatalogFilterPaginatedSpecification : Specification { - public class CatalogFilterPaginatedSpecification : Specification + public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId) + : base() { - public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId) - : base() + if (take == 0) { - if (take == 0) - { - take = int.MaxValue; - } - Query - .Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) && - (!typeId.HasValue || i.CatalogTypeId == typeId)) - .Skip(skip).Take(take); + take = int.MaxValue; } + Query + .Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) && + (!typeId.HasValue || i.CatalogTypeId == typeId)) + .Skip(skip).Take(take); } } diff --git a/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs b/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs index 9a204ad68..4cdc2eff1 100644 --- a/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs +++ b/src/ApplicationCore/Specifications/CatalogFilterSpecification.cs @@ -1,14 +1,13 @@ using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities; -namespace Microsoft.eShopWeb.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.ApplicationCore.Specifications; + +public class CatalogFilterSpecification : Specification { - public class CatalogFilterSpecification : Specification + public CatalogFilterSpecification(int? brandId, int? typeId) { - public CatalogFilterSpecification(int? brandId, int? typeId) - { - Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) && - (!typeId.HasValue || i.CatalogTypeId == typeId)); - } + Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) && + (!typeId.HasValue || i.CatalogTypeId == typeId)); } } diff --git a/src/ApplicationCore/Specifications/CatalogItemNameSpecification.cs b/src/ApplicationCore/Specifications/CatalogItemNameSpecification.cs index 4832ad86d..a445d5173 100644 --- a/src/ApplicationCore/Specifications/CatalogItemNameSpecification.cs +++ b/src/ApplicationCore/Specifications/CatalogItemNameSpecification.cs @@ -1,13 +1,12 @@ using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities; -namespace Microsoft.eShopWeb.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.ApplicationCore.Specifications; + +public class CatalogItemNameSpecification : Specification { - public class CatalogItemNameSpecification : Specification + public CatalogItemNameSpecification(string catalogItemName) { - public CatalogItemNameSpecification(string catalogItemName) - { - Query.Where(item => catalogItemName == item.Name); - } + Query.Where(item => catalogItemName == item.Name); } } diff --git a/src/ApplicationCore/Specifications/CatalogItemsSpecification.cs b/src/ApplicationCore/Specifications/CatalogItemsSpecification.cs index 05bb1ecb6..584cbb53f 100644 --- a/src/ApplicationCore/Specifications/CatalogItemsSpecification.cs +++ b/src/ApplicationCore/Specifications/CatalogItemsSpecification.cs @@ -1,15 +1,14 @@ -using Ardalis.Specification; -using Microsoft.eShopWeb.ApplicationCore.Entities; -using System; +using System; using System.Linq; +using Ardalis.Specification; +using Microsoft.eShopWeb.ApplicationCore.Entities; + +namespace Microsoft.eShopWeb.ApplicationCore.Specifications; -namespace Microsoft.eShopWeb.ApplicationCore.Specifications +public class CatalogItemsSpecification : Specification { - public class CatalogItemsSpecification : Specification + public CatalogItemsSpecification(params int[] ids) { - public CatalogItemsSpecification(params int[] ids) - { - Query.Where(c => ids.Contains(c.Id)); - } + Query.Where(c => ids.Contains(c.Id)); } } diff --git a/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs b/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs index 12b7659e5..3ca104811 100644 --- a/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs +++ b/src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs @@ -1,15 +1,14 @@ using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.ApplicationCore.Specifications; + +public class CustomerOrdersWithItemsSpecification : Specification { - public class CustomerOrdersWithItemsSpecification : Specification + public CustomerOrdersWithItemsSpecification(string buyerId) { - public CustomerOrdersWithItemsSpecification(string buyerId) - { - Query.Where(o => o.BuyerId == buyerId) - .Include(o => o.OrderItems) - .ThenInclude(i => i.ItemOrdered); - } + Query.Where(o => o.BuyerId == buyerId) + .Include(o => o.OrderItems) + .ThenInclude(i => i.ItemOrdered); } } diff --git a/src/ApplicationCore/Specifications/OrderWithItemsByIdSpec.cs b/src/ApplicationCore/Specifications/OrderWithItemsByIdSpec.cs index 73aeff7a2..2f0d888f4 100644 --- a/src/ApplicationCore/Specifications/OrderWithItemsByIdSpec.cs +++ b/src/ApplicationCore/Specifications/OrderWithItemsByIdSpec.cs @@ -1,16 +1,15 @@ using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.ApplicationCore.Specifications; + +public class OrderWithItemsByIdSpec : Specification, ISingleResultSpecification { - public class OrderWithItemsByIdSpec : Specification, ISingleResultSpecification + public OrderWithItemsByIdSpec(int orderId) { - public OrderWithItemsByIdSpec(int orderId) - { - Query - .Where(order => order.Id == orderId) - .Include(o => o.OrderItems) - .ThenInclude(i => i.ItemOrdered); - } + Query + .Where(order => order.Id == orderId) + .Include(o => o.OrderItems) + .ThenInclude(i => i.ItemOrdered); } } diff --git a/src/BlazorAdmin/BlazorAdmin.csproj b/src/BlazorAdmin/BlazorAdmin.csproj index e54253abe..58a5b69f8 100644 --- a/src/BlazorAdmin/BlazorAdmin.csproj +++ b/src/BlazorAdmin/BlazorAdmin.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 diff --git a/src/BlazorAdmin/CustomAuthStateProvider.cs b/src/BlazorAdmin/CustomAuthStateProvider.cs index f1565c404..b12d23251 100644 --- a/src/BlazorAdmin/CustomAuthStateProvider.cs +++ b/src/BlazorAdmin/CustomAuthStateProvider.cs @@ -1,87 +1,86 @@ -using BlazorShared.Authorization; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.Extensions.Logging; -using System; +using System; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Claims; using System.Threading.Tasks; +using BlazorShared.Authorization; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.Extensions.Logging; + +namespace BlazorAdmin; -namespace BlazorAdmin +public class CustomAuthStateProvider : AuthenticationStateProvider { - public class CustomAuthStateProvider : AuthenticationStateProvider - { - // TODO: Get Default Cache Duration from Config - private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60); + // TODO: Get Default Cache Duration from Config + private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60); - private readonly HttpClient _httpClient; - private readonly ILogger _logger; + private readonly HttpClient _httpClient; + private readonly ILogger _logger; - private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0); - private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity()); + private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0); + private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity()); - public CustomAuthStateProvider(HttpClient httpClient, - ILogger logger) - { - _httpClient = httpClient; - _logger = logger; - } + public CustomAuthStateProvider(HttpClient httpClient, + ILogger logger) + { + _httpClient = httpClient; + _logger = logger; + } - public override async Task GetAuthenticationStateAsync() + public override async Task GetAuthenticationStateAsync() + { + return new AuthenticationState(await GetUser(useCache: true)); + } + + private async ValueTask GetUser(bool useCache = false) + { + var now = DateTimeOffset.Now; + if (useCache && now < _userLastCheck + UserCacheRefreshInterval) { - return new AuthenticationState(await GetUser(useCache: true)); + return _cachedUser; } - private async ValueTask GetUser(bool useCache = false) - { - var now = DateTimeOffset.Now; - if (useCache && now < _userLastCheck + UserCacheRefreshInterval) - { - return _cachedUser; - } + _cachedUser = await FetchUser(); + _userLastCheck = now; - _cachedUser = await FetchUser(); - _userLastCheck = now; + return _cachedUser; + } - return _cachedUser; - } + private async Task FetchUser() + { + UserInfo user = null; - private async Task FetchUser() + try { - UserInfo user = null; - - try - { - _logger.LogInformation("Fetching user details from web api."); - user = await _httpClient.GetFromJsonAsync("User"); - } - catch (Exception exc) - { - _logger.LogWarning(exc, "Fetching user failed."); - } + _logger.LogInformation("Fetching user details from web api."); + user = await _httpClient.GetFromJsonAsync("User"); + } + catch (Exception exc) + { + _logger.LogWarning(exc, "Fetching user failed."); + } - if (user == null || !user.IsAuthenticated) - { - return null; - } + if (user == null || !user.IsAuthenticated) + { + return null; + } - var identity = new ClaimsIdentity( - nameof(CustomAuthStateProvider), - user.NameClaimType, - user.RoleClaimType); + var identity = new ClaimsIdentity( + nameof(CustomAuthStateProvider), + user.NameClaimType, + user.RoleClaimType); - if (user.Claims != null) + if (user.Claims != null) + { + foreach (var claim in user.Claims) { - foreach (var claim in user.Claims) - { - identity.AddClaim(new Claim(claim.Type, claim.Value)); - } + identity.AddClaim(new Claim(claim.Type, claim.Value)); } + } - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token); - return new ClaimsPrincipal(identity); - } + return new ClaimsPrincipal(identity); } } diff --git a/src/BlazorAdmin/Helpers/BlazorComponent.cs b/src/BlazorAdmin/Helpers/BlazorComponent.cs index 43302c5c8..36d714133 100644 --- a/src/BlazorAdmin/Helpers/BlazorComponent.cs +++ b/src/BlazorAdmin/Helpers/BlazorComponent.cs @@ -1,26 +1,25 @@ using Microsoft.AspNetCore.Components; -namespace BlazorAdmin.Helpers -{ - public class BlazorComponent : ComponentBase - { - private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance; +namespace BlazorAdmin.Helpers; - protected override void OnInitialized() - { - _refresh.RefreshRequested += DoRefresh; - base.OnInitialized(); - } +public class BlazorComponent : ComponentBase +{ + private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance; - public void CallRequestRefresh() - { - _refresh.CallRequestRefresh(); - } + protected override void OnInitialized() + { + _refresh.RefreshRequested += DoRefresh; + base.OnInitialized(); + } - private void DoRefresh() - { - StateHasChanged(); - } + public void CallRequestRefresh() + { + _refresh.CallRequestRefresh(); + } + private void DoRefresh() + { + StateHasChanged(); } + } diff --git a/src/BlazorAdmin/Helpers/BlazorLayoutComponent.cs b/src/BlazorAdmin/Helpers/BlazorLayoutComponent.cs index ca051f806..e6288321c 100644 --- a/src/BlazorAdmin/Helpers/BlazorLayoutComponent.cs +++ b/src/BlazorAdmin/Helpers/BlazorLayoutComponent.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Components; -namespace BlazorAdmin.Helpers +namespace BlazorAdmin.Helpers; + +public class BlazorLayoutComponent : LayoutComponentBase { - public class BlazorLayoutComponent : LayoutComponentBase - { - private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance; + private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance; - protected override void OnInitialized() - { - _refresh.RefreshRequested += DoRefresh; - base.OnInitialized(); - } + protected override void OnInitialized() + { + _refresh.RefreshRequested += DoRefresh; + base.OnInitialized(); + } - public void CallRequestRefresh() - { - _refresh.CallRequestRefresh(); - } + public void CallRequestRefresh() + { + _refresh.CallRequestRefresh(); + } - private void DoRefresh() - { - StateHasChanged(); - } + private void DoRefresh() + { + StateHasChanged(); } } diff --git a/src/BlazorAdmin/Helpers/RefreshBroadcast.cs b/src/BlazorAdmin/Helpers/RefreshBroadcast.cs index ef6a1ea1c..a31e96c29 100644 --- a/src/BlazorAdmin/Helpers/RefreshBroadcast.cs +++ b/src/BlazorAdmin/Helpers/RefreshBroadcast.cs @@ -1,24 +1,23 @@ using System; -namespace BlazorAdmin.Helpers +namespace BlazorAdmin.Helpers; + +internal sealed class RefreshBroadcast { - internal sealed class RefreshBroadcast - { - private static readonly Lazy - Lazy = - new Lazy - (() => new RefreshBroadcast()); + private static readonly Lazy + Lazy = + new Lazy + (() => new RefreshBroadcast()); - public static RefreshBroadcast Instance => Lazy.Value; + public static RefreshBroadcast Instance => Lazy.Value; - private RefreshBroadcast() - { - } + private RefreshBroadcast() + { + } - public event Action RefreshRequested; - public void CallRequestRefresh() - { - RefreshRequested?.Invoke(); - } + public event Action RefreshRequested; + public void CallRequestRefresh() + { + RefreshRequested?.Invoke(); } } diff --git a/src/BlazorAdmin/Helpers/ToastComponent.cs b/src/BlazorAdmin/Helpers/ToastComponent.cs index 5babca183..33fd667c7 100644 --- a/src/BlazorAdmin/Helpers/ToastComponent.cs +++ b/src/BlazorAdmin/Helpers/ToastComponent.cs @@ -1,88 +1,87 @@ -using BlazorAdmin.Services; +using System; +using BlazorAdmin.Services; using Microsoft.AspNetCore.Components; -using System; -namespace BlazorAdmin.Helpers +namespace BlazorAdmin.Helpers; + +public class ToastComponent : ComponentBase, IDisposable { - public class ToastComponent : ComponentBase, IDisposable + [Inject] + ToastService ToastService { - [Inject] - ToastService ToastService - { - get; - set; - } - protected string Heading - { - get; - set; - } - protected string Message - { - get; - set; - } - protected bool IsVisible - { - get; - set; - } - protected string BackgroundCssClass - { - get; - set; - } - protected string IconCssClass - { - get; - set; - } - protected override void OnInitialized() - { - ToastService.OnShow += ShowToast; - ToastService.OnHide += HideToast; - } - private void ShowToast(string message, ToastLevel level) - { - BuildToastSettings(level, message); - IsVisible = true; - StateHasChanged(); - } - private void HideToast() - { - IsVisible = false; - StateHasChanged(); - } - private void BuildToastSettings(ToastLevel level, string message) - { - switch (level) - { - case ToastLevel.Info: - BackgroundCssClass = "bg-info"; - IconCssClass = "info"; - Heading = "Info"; - break; - case ToastLevel.Success: - BackgroundCssClass = "bg-success"; - IconCssClass = "check"; - Heading = "Success"; - break; - case ToastLevel.Warning: - BackgroundCssClass = "bg-warning"; - IconCssClass = "exclamation"; - Heading = "Warning"; - break; - case ToastLevel.Error: - BackgroundCssClass = "bg-danger"; - IconCssClass = "times"; - Heading = "Error"; - break; - } - Message = message; - } - public void Dispose() + get; + set; + } + protected string Heading + { + get; + set; + } + protected string Message + { + get; + set; + } + protected bool IsVisible + { + get; + set; + } + protected string BackgroundCssClass + { + get; + set; + } + protected string IconCssClass + { + get; + set; + } + protected override void OnInitialized() + { + ToastService.OnShow += ShowToast; + ToastService.OnHide += HideToast; + } + private void ShowToast(string message, ToastLevel level) + { + BuildToastSettings(level, message); + IsVisible = true; + StateHasChanged(); + } + private void HideToast() + { + IsVisible = false; + StateHasChanged(); + } + private void BuildToastSettings(ToastLevel level, string message) + { + switch (level) { - ToastService.OnShow -= ShowToast; + case ToastLevel.Info: + BackgroundCssClass = "bg-info"; + IconCssClass = "info"; + Heading = "Info"; + break; + case ToastLevel.Success: + BackgroundCssClass = "bg-success"; + IconCssClass = "check"; + Heading = "Success"; + break; + case ToastLevel.Warning: + BackgroundCssClass = "bg-warning"; + IconCssClass = "exclamation"; + Heading = "Warning"; + break; + case ToastLevel.Error: + BackgroundCssClass = "bg-danger"; + IconCssClass = "times"; + Heading = "Error"; + break; } + Message = message; + } + public void Dispose() + { + ToastService.OnShow -= ShowToast; } } diff --git a/src/BlazorAdmin/JavaScript/Cookies.cs b/src/BlazorAdmin/JavaScript/Cookies.cs index f6b068eb7..7d8742cb7 100644 --- a/src/BlazorAdmin/JavaScript/Cookies.cs +++ b/src/BlazorAdmin/JavaScript/Cookies.cs @@ -1,25 +1,24 @@ -using Microsoft.JSInterop; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.JSInterop; -namespace BlazorAdmin.JavaScript +namespace BlazorAdmin.JavaScript; + +public class Cookies { - public class Cookies - { - private readonly IJSRuntime _jsRuntime; + private readonly IJSRuntime _jsRuntime; - public Cookies(IJSRuntime jsRuntime) - { - _jsRuntime = jsRuntime; - } + public Cookies(IJSRuntime jsRuntime) + { + _jsRuntime = jsRuntime; + } - public async Task DeleteCookie(string name) - { - await _jsRuntime.InvokeAsync(JSInteropConstants.DeleteCookie, name); - } + public async Task DeleteCookie(string name) + { + await _jsRuntime.InvokeAsync(JSInteropConstants.DeleteCookie, name); + } - public async Task GetCookie(string name) - { - return await _jsRuntime.InvokeAsync(JSInteropConstants.GetCookie, name); - } + public async Task GetCookie(string name) + { + return await _jsRuntime.InvokeAsync(JSInteropConstants.GetCookie, name); } } diff --git a/src/BlazorAdmin/JavaScript/Css.cs b/src/BlazorAdmin/JavaScript/Css.cs index 9c725ef98..11d8763bd 100644 --- a/src/BlazorAdmin/JavaScript/Css.cs +++ b/src/BlazorAdmin/JavaScript/Css.cs @@ -1,25 +1,24 @@ -using Microsoft.JSInterop; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.JSInterop; -namespace BlazorAdmin.JavaScript +namespace BlazorAdmin.JavaScript; + +public class Css { - public class Css + private readonly IJSRuntime _jsRuntime; + + public Css(IJSRuntime jsRuntime) { - private readonly IJSRuntime _jsRuntime; + _jsRuntime = jsRuntime; + } - public Css(IJSRuntime jsRuntime) - { - _jsRuntime = jsRuntime; - } - - public async Task ShowBodyOverflow() - { - await _jsRuntime.InvokeAsync(JSInteropConstants.ShowBodyOverflow); - } + public async Task ShowBodyOverflow() + { + await _jsRuntime.InvokeAsync(JSInteropConstants.ShowBodyOverflow); + } - public async Task HideBodyOverflow() - { - return await _jsRuntime.InvokeAsync(JSInteropConstants.HideBodyOverflow); - } + public async Task HideBodyOverflow() + { + return await _jsRuntime.InvokeAsync(JSInteropConstants.HideBodyOverflow); } } diff --git a/src/BlazorAdmin/JavaScript/JSInteropConstants.cs b/src/BlazorAdmin/JavaScript/JSInteropConstants.cs index 44c4dadde..63a3d0cb5 100644 --- a/src/BlazorAdmin/JavaScript/JSInteropConstants.cs +++ b/src/BlazorAdmin/JavaScript/JSInteropConstants.cs @@ -1,11 +1,10 @@ -namespace BlazorAdmin.JavaScript +namespace BlazorAdmin.JavaScript; + +public static class JSInteropConstants { - public static class JSInteropConstants - { - public static string DeleteCookie => "deleteCookie"; - public static string GetCookie => "getCookie"; - public static string RouteOutside => "routeOutside"; - public static string HideBodyOverflow => "hideBodyOverflow"; - public static string ShowBodyOverflow => "showBodyOverflow"; - } + public static string DeleteCookie => "deleteCookie"; + public static string GetCookie => "getCookie"; + public static string RouteOutside => "routeOutside"; + public static string HideBodyOverflow => "hideBodyOverflow"; + public static string ShowBodyOverflow => "showBodyOverflow"; } diff --git a/src/BlazorAdmin/JavaScript/Route.cs b/src/BlazorAdmin/JavaScript/Route.cs index e8d5623be..3694545ad 100644 --- a/src/BlazorAdmin/JavaScript/Route.cs +++ b/src/BlazorAdmin/JavaScript/Route.cs @@ -1,20 +1,19 @@ -using Microsoft.JSInterop; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.JSInterop; -namespace BlazorAdmin.JavaScript +namespace BlazorAdmin.JavaScript; + +public class Route { - public class Route - { - private readonly IJSRuntime _jsRuntime; + private readonly IJSRuntime _jsRuntime; - public Route(IJSRuntime jsRuntime) - { - _jsRuntime = jsRuntime; - } + public Route(IJSRuntime jsRuntime) + { + _jsRuntime = jsRuntime; + } - public async Task RouteOutside(string path) - { - await _jsRuntime.InvokeAsync(JSInteropConstants.RouteOutside, path); - } + public async Task RouteOutside(string path) + { + await _jsRuntime.InvokeAsync(JSInteropConstants.RouteOutside, path); } } diff --git a/src/BlazorAdmin/Pages/CatalogItemPage/List.razor.cs b/src/BlazorAdmin/Pages/CatalogItemPage/List.razor.cs index 73a8d349e..05fcbc6ee 100644 --- a/src/BlazorAdmin/Pages/CatalogItemPage/List.razor.cs +++ b/src/BlazorAdmin/Pages/CatalogItemPage/List.razor.cs @@ -1,69 +1,68 @@ -using BlazorAdmin.Helpers; +using System.Collections.Generic; +using System.Threading.Tasks; +using BlazorAdmin.Helpers; using BlazorShared.Interfaces; using BlazorShared.Models; -using System.Collections.Generic; -using System.Threading.Tasks; -namespace BlazorAdmin.Pages.CatalogItemPage +namespace BlazorAdmin.Pages.CatalogItemPage; + +public partial class List : BlazorComponent { - public partial class List : BlazorComponent - { - [Microsoft.AspNetCore.Components.Inject] - public ICatalogItemService CatalogItemService { get; set; } + [Microsoft.AspNetCore.Components.Inject] + public ICatalogItemService CatalogItemService { get; set; } - [Microsoft.AspNetCore.Components.Inject] - public ICatalogLookupDataService CatalogBrandService { get; set; } + [Microsoft.AspNetCore.Components.Inject] + public ICatalogLookupDataService CatalogBrandService { get; set; } - [Microsoft.AspNetCore.Components.Inject] - public ICatalogLookupDataService CatalogTypeService { get; set; } + [Microsoft.AspNetCore.Components.Inject] + public ICatalogLookupDataService CatalogTypeService { get; set; } - private List catalogItems = new List(); - private List catalogTypes = new List(); - private List catalogBrands = new List(); + private List catalogItems = new List(); + private List catalogTypes = new List(); + private List catalogBrands = new List(); - private Edit EditComponent { get; set; } - private Delete DeleteComponent { get; set; } - private Details DetailsComponent { get; set; } - private Create CreateComponent { get; set; } + private Edit EditComponent { get; set; } + private Delete DeleteComponent { get; set; } + private Details DetailsComponent { get; set; } + private Create CreateComponent { get; set; } - protected override async Task OnAfterRenderAsync(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) { - if (firstRender) - { - catalogItems = await CatalogItemService.List(); - catalogTypes = await CatalogTypeService.List(); - catalogBrands = await CatalogBrandService.List(); - - CallRequestRefresh(); - } + catalogItems = await CatalogItemService.List(); + catalogTypes = await CatalogTypeService.List(); + catalogBrands = await CatalogBrandService.List(); - await base.OnAfterRenderAsync(firstRender); + CallRequestRefresh(); } - private async void DetailsClick(int id) - { - await DetailsComponent.Open(id); - } + await base.OnAfterRenderAsync(firstRender); + } - private async Task CreateClick() - { - await CreateComponent.Open(); - } + private async void DetailsClick(int id) + { + await DetailsComponent.Open(id); + } - private async Task EditClick(int id) - { - await EditComponent.Open(id); - } + private async Task CreateClick() + { + await CreateComponent.Open(); + } - private async Task DeleteClick(int id) - { - await DeleteComponent.Open(id); - } + private async Task EditClick(int id) + { + await EditComponent.Open(id); + } - private async Task ReloadCatalogItems() - { - catalogItems = await CatalogItemService.List(); - StateHasChanged(); - } + private async Task DeleteClick(int id) + { + await DeleteComponent.Open(id); + } + + private async Task ReloadCatalogItems() + { + catalogItems = await CatalogItemService.List(); + StateHasChanged(); } } diff --git a/src/BlazorAdmin/Program.cs b/src/BlazorAdmin/Program.cs index 042a27575..a002838e4 100644 --- a/src/BlazorAdmin/Program.cs +++ b/src/BlazorAdmin/Program.cs @@ -1,3 +1,6 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; using BlazorAdmin.Services; using Blazored.LocalStorage; using BlazorShared; @@ -7,50 +10,46 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System; -using System.Net.Http; -using System.Threading.Tasks; -namespace BlazorAdmin +namespace BlazorAdmin; + +public class Program { - public class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) - { - var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.RootComponents.Add("#admin"); + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("#admin"); - var baseUrlConfig = new BaseUrlConfiguration(); - builder.Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); - builder.Services.AddScoped(sp => baseUrlConfig); + var baseUrlConfig = new BaseUrlConfiguration(); + builder.Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); + builder.Services.AddScoped(sp => baseUrlConfig); - builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); - builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); - builder.Services.AddBlazoredLocalStorage(); + builder.Services.AddBlazoredLocalStorage(); - builder.Services.AddAuthorizationCore(); - builder.Services.AddScoped(); - builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService()); + builder.Services.AddAuthorizationCore(); + builder.Services.AddScoped(); + builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService()); - builder.Services.AddBlazorServices(); + builder.Services.AddBlazorServices(); - builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging")); + builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging")); - await ClearLocalStorageCache(builder.Services); + await ClearLocalStorageCache(builder.Services); - await builder.Build().RunAsync(); - } + await builder.Build().RunAsync(); + } - private static async Task ClearLocalStorageCache(IServiceCollection services) - { - var sp = services.BuildServiceProvider(); - var localStorageService = sp.GetRequiredService(); + private static async Task ClearLocalStorageCache(IServiceCollection services) + { + var sp = services.BuildServiceProvider(); + var localStorageService = sp.GetRequiredService(); - await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name); - await localStorageService.RemoveItemAsync(typeof(CatalogType).Name); - } + await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name); + await localStorageService.RemoveItemAsync(typeof(CatalogType).Name); } } diff --git a/src/BlazorAdmin/Services/CacheEntry.cs b/src/BlazorAdmin/Services/CacheEntry.cs index 97abd8ef7..246671d65 100644 --- a/src/BlazorAdmin/Services/CacheEntry.cs +++ b/src/BlazorAdmin/Services/CacheEntry.cs @@ -1,19 +1,18 @@ using System; -namespace BlazorAdmin.Services +namespace BlazorAdmin.Services; + +public class CacheEntry { - public class CacheEntry + public CacheEntry(T item) + { + Value = item; + } + public CacheEntry() { - public CacheEntry(T item) - { - Value = item; - } - public CacheEntry() - { - - } - public T Value { get; set; } - public DateTime DateCreated { get; set; } = DateTime.UtcNow; } + + public T Value { get; set; } + public DateTime DateCreated { get; set; } = DateTime.UtcNow; } diff --git a/src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs b/src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs index 885cb940f..a81d3c7bc 100644 --- a/src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs +++ b/src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs @@ -1,114 +1,113 @@ -using Blazored.LocalStorage; -using BlazorShared.Interfaces; -using BlazorShared.Models; -using Microsoft.Extensions.Logging; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Blazored.LocalStorage; +using BlazorShared.Interfaces; +using BlazorShared.Models; +using Microsoft.Extensions.Logging; -namespace BlazorAdmin.Services +namespace BlazorAdmin.Services; + +public class CachedCatalogItemServiceDecorator : ICatalogItemService { - public class CachedCatalogItemServiceDecorator : ICatalogItemService - { - private readonly ILocalStorageService _localStorageService; - private readonly CatalogItemService _catalogItemService; - private ILogger _logger; + private readonly ILocalStorageService _localStorageService; + private readonly CatalogItemService _catalogItemService; + private ILogger _logger; - public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService, - CatalogItemService catalogItemService, - ILogger logger) - { - _localStorageService = localStorageService; - _catalogItemService = catalogItemService; - _logger = logger; - } + public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService, + CatalogItemService catalogItemService, + ILogger logger) + { + _localStorageService = localStorageService; + _catalogItemService = catalogItemService; + _logger = logger; + } - public async Task> ListPaged(int pageSize) + public async Task> ListPaged(int pageSize) + { + string key = "items"; + var cacheEntry = await _localStorageService.GetItemAsync>>(key); + if (cacheEntry != null) { - string key = "items"; - var cacheEntry = await _localStorageService.GetItemAsync>>(key); - if (cacheEntry != null) + _logger.LogInformation("Loading items from local storage."); + if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow) { - _logger.LogInformation("Loading items from local storage."); - if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow) - { - return cacheEntry.Value; - } - else - { - _logger.LogInformation($"Loading {key} from local storage."); - await _localStorageService.RemoveItemAsync(key); - } + return cacheEntry.Value; + } + else + { + _logger.LogInformation($"Loading {key} from local storage."); + await _localStorageService.RemoveItemAsync(key); } - - var items = await _catalogItemService.ListPaged(pageSize); - var entry = new CacheEntry>(items); - await _localStorageService.SetItemAsync(key, entry); - return items; } - public async Task> List() + var items = await _catalogItemService.ListPaged(pageSize); + var entry = new CacheEntry>(items); + await _localStorageService.SetItemAsync(key, entry); + return items; + } + + public async Task> List() + { + string key = "items"; + var cacheEntry = await _localStorageService.GetItemAsync>>(key); + if (cacheEntry != null) { - string key = "items"; - var cacheEntry = await _localStorageService.GetItemAsync>>(key); - if (cacheEntry != null) + _logger.LogInformation("Loading items from local storage."); + if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow) { - _logger.LogInformation("Loading items from local storage."); - if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow) - { - return cacheEntry.Value; - } - else - { - _logger.LogInformation($"Loading {key} from local storage."); - await _localStorageService.RemoveItemAsync(key); - } + return cacheEntry.Value; + } + else + { + _logger.LogInformation($"Loading {key} from local storage."); + await _localStorageService.RemoveItemAsync(key); } - - var items = await _catalogItemService.List(); - var entry = new CacheEntry>(items); - await _localStorageService.SetItemAsync(key, entry); - return items; } - public async Task GetById(int id) - { - return (await List()).FirstOrDefault(x => x.Id == id); - } + var items = await _catalogItemService.List(); + var entry = new CacheEntry>(items); + await _localStorageService.SetItemAsync(key, entry); + return items; + } - public async Task Create(CreateCatalogItemRequest catalogItem) - { - var result = await _catalogItemService.Create(catalogItem); - await RefreshLocalStorageList(); + public async Task GetById(int id) + { + return (await List()).FirstOrDefault(x => x.Id == id); + } - return result; - } + public async Task Create(CreateCatalogItemRequest catalogItem) + { + var result = await _catalogItemService.Create(catalogItem); + await RefreshLocalStorageList(); - public async Task Edit(CatalogItem catalogItem) - { - var result = await _catalogItemService.Edit(catalogItem); - await RefreshLocalStorageList(); + return result; + } - return result; - } + public async Task Edit(CatalogItem catalogItem) + { + var result = await _catalogItemService.Edit(catalogItem); + await RefreshLocalStorageList(); - public async Task Delete(int id) - { - var result = await _catalogItemService.Delete(id); - await RefreshLocalStorageList(); + return result; + } - return result; - } + public async Task Delete(int id) + { + var result = await _catalogItemService.Delete(id); + await RefreshLocalStorageList(); - private async Task RefreshLocalStorageList() - { - string key = "items"; + return result; + } - await _localStorageService.RemoveItemAsync(key); - var items = await _catalogItemService.List(); - var entry = new CacheEntry>(items); - await _localStorageService.SetItemAsync(key, entry); - } + private async Task RefreshLocalStorageList() + { + string key = "items"; + + await _localStorageService.RemoveItemAsync(key); + var items = await _catalogItemService.List(); + var entry = new CacheEntry>(items); + await _localStorageService.SetItemAsync(key, entry); } } diff --git a/src/BlazorAdmin/Services/CachedCatalogLookupDataServiceDecorator .cs b/src/BlazorAdmin/Services/CachedCatalogLookupDataServiceDecorator .cs index aa93621e0..767763c6b 100644 --- a/src/BlazorAdmin/Services/CachedCatalogLookupDataServiceDecorator .cs +++ b/src/BlazorAdmin/Services/CachedCatalogLookupDataServiceDecorator .cs @@ -1,53 +1,52 @@ -using Blazored.LocalStorage; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Blazored.LocalStorage; using BlazorShared.Interfaces; using BlazorShared.Models; using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -namespace BlazorAdmin.Services +namespace BlazorAdmin.Services; + +public class CachedCatalogLookupDataServiceDecorator + : ICatalogLookupDataService + where TLookupData : LookupData + where TReponse : ILookupDataResponse { - public class CachedCatalogLookupDataServiceDecorator - : ICatalogLookupDataService - where TLookupData : LookupData - where TReponse : ILookupDataResponse - { - private readonly ILocalStorageService _localStorageService; - private readonly CatalogLookupDataService _catalogTypeService; - private ILogger> _logger; + private readonly ILocalStorageService _localStorageService; + private readonly CatalogLookupDataService _catalogTypeService; + private ILogger> _logger; - public CachedCatalogLookupDataServiceDecorator(ILocalStorageService localStorageService, - CatalogLookupDataService catalogTypeService, - ILogger> logger) - { - _localStorageService = localStorageService; - _catalogTypeService = catalogTypeService; - _logger = logger; - } + public CachedCatalogLookupDataServiceDecorator(ILocalStorageService localStorageService, + CatalogLookupDataService catalogTypeService, + ILogger> logger) + { + _localStorageService = localStorageService; + _catalogTypeService = catalogTypeService; + _logger = logger; + } - public async Task> List() + public async Task> List() + { + string key = typeof(TLookupData).Name; + var cacheEntry = await _localStorageService.GetItemAsync>>(key); + if (cacheEntry != null) { - string key = typeof(TLookupData).Name; - var cacheEntry = await _localStorageService.GetItemAsync>>(key); - if (cacheEntry != null) + _logger.LogInformation($"Loading {key} from local storage."); + if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow) { - _logger.LogInformation($"Loading {key} from local storage."); - if (cacheEntry.DateCreated.AddMinutes(1) > DateTime.UtcNow) - { - return cacheEntry.Value; - } - else - { - _logger.LogInformation($"Cache expired; removing {key} from local storage."); - await _localStorageService.RemoveItemAsync(key); - } + return cacheEntry.Value; + } + else + { + _logger.LogInformation($"Cache expired; removing {key} from local storage."); + await _localStorageService.RemoveItemAsync(key); } - - var types = await _catalogTypeService.List(); - var entry = new CacheEntry>(types); - await _localStorageService.SetItemAsync(key, entry); - return types; } + + var types = await _catalogTypeService.List(); + var entry = new CacheEntry>(types); + await _localStorageService.SetItemAsync(key, entry); + return types; } } diff --git a/src/BlazorAdmin/Services/CatalogItemService.cs b/src/BlazorAdmin/Services/CatalogItemService.cs index b5b5dddc3..f3ebfd8c3 100644 --- a/src/BlazorAdmin/Services/CatalogItemService.cs +++ b/src/BlazorAdmin/Services/CatalogItemService.cs @@ -1,97 +1,96 @@ -using BlazorShared.Interfaces; -using BlazorShared.Models; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using BlazorShared.Interfaces; +using BlazorShared.Models; +using Microsoft.Extensions.Logging; -namespace BlazorAdmin.Services +namespace BlazorAdmin.Services; + +public class CatalogItemService : ICatalogItemService { - public class CatalogItemService : ICatalogItemService + private readonly ICatalogLookupDataService _brandService; + private readonly ICatalogLookupDataService _typeService; + private readonly HttpService _httpService; + private readonly ILogger _logger; + + public CatalogItemService(ICatalogLookupDataService brandService, + ICatalogLookupDataService typeService, + HttpService httpService, + ILogger logger) { - private readonly ICatalogLookupDataService _brandService; - private readonly ICatalogLookupDataService _typeService; - private readonly HttpService _httpService; - private readonly ILogger _logger; + _brandService = brandService; + _typeService = typeService; + _httpService = httpService; + _logger = logger; + } - public CatalogItemService(ICatalogLookupDataService brandService, - ICatalogLookupDataService typeService, - HttpService httpService, - ILogger logger) - { - _brandService = brandService; - _typeService = typeService; - _httpService = httpService; - _logger = logger; - } + public async Task Create(CreateCatalogItemRequest catalogItem) + { + var response = await _httpService.HttpPost("catalog-items", catalogItem); + return response?.CatalogItem; + } - public async Task Create(CreateCatalogItemRequest catalogItem) - { - var response = await _httpService.HttpPost("catalog-items", catalogItem); - return response?.CatalogItem; - } + public async Task Edit(CatalogItem catalogItem) + { + return (await _httpService.HttpPut("catalog-items", catalogItem)).CatalogItem; + } - public async Task Edit(CatalogItem catalogItem) - { - return (await _httpService.HttpPut("catalog-items", catalogItem)).CatalogItem; - } + public async Task Delete(int catalogItemId) + { + return (await _httpService.HttpDelete("catalog-items", catalogItemId)).Status; + } - public async Task Delete(int catalogItemId) - { - return (await _httpService.HttpDelete("catalog-items", catalogItemId)).Status; - } + public async Task GetById(int id) + { + var brandListTask = _brandService.List(); + var typeListTask = _typeService.List(); + var itemGetTask = _httpService.HttpGet($"catalog-items/{id}"); + await Task.WhenAll(brandListTask, typeListTask, itemGetTask); + var brands = brandListTask.Result; + var types = typeListTask.Result; + var catalogItem = itemGetTask.Result.CatalogItem; + catalogItem.CatalogBrand = brands.FirstOrDefault(b => b.Id == catalogItem.CatalogBrandId)?.Name; + catalogItem.CatalogType = types.FirstOrDefault(t => t.Id == catalogItem.CatalogTypeId)?.Name; + return catalogItem; + } - public async Task GetById(int id) - { - var brandListTask = _brandService.List(); - var typeListTask = _typeService.List(); - var itemGetTask = _httpService.HttpGet($"catalog-items/{id}"); - await Task.WhenAll(brandListTask, typeListTask, itemGetTask); - var brands = brandListTask.Result; - var types = typeListTask.Result; - var catalogItem = itemGetTask.Result.CatalogItem; - catalogItem.CatalogBrand = brands.FirstOrDefault(b => b.Id == catalogItem.CatalogBrandId)?.Name; - catalogItem.CatalogType = types.FirstOrDefault(t => t.Id == catalogItem.CatalogTypeId)?.Name; - return catalogItem; - } + public async Task> ListPaged(int pageSize) + { + _logger.LogInformation("Fetching catalog items from API."); - public async Task> ListPaged(int pageSize) + var brandListTask = _brandService.List(); + var typeListTask = _typeService.List(); + var itemListTask = _httpService.HttpGet($"catalog-items?PageSize=10"); + await Task.WhenAll(brandListTask, typeListTask, itemListTask); + var brands = brandListTask.Result; + var types = typeListTask.Result; + var items = itemListTask.Result.CatalogItems; + foreach (var item in items) { - _logger.LogInformation("Fetching catalog items from API."); - - var brandListTask = _brandService.List(); - var typeListTask = _typeService.List(); - var itemListTask = _httpService.HttpGet($"catalog-items?PageSize=10"); - await Task.WhenAll(brandListTask, typeListTask, itemListTask); - var brands = brandListTask.Result; - var types = typeListTask.Result; - var items = itemListTask.Result.CatalogItems; - foreach (var item in items) - { - item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name; - item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name; - } - return items; + item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name; + item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name; } + return items; + } - public async Task> List() - { - _logger.LogInformation("Fetching catalog items from API."); + public async Task> List() + { + _logger.LogInformation("Fetching catalog items from API."); - var brandListTask = _brandService.List(); - var typeListTask = _typeService.List(); - var itemListTask = _httpService.HttpGet($"catalog-items"); - await Task.WhenAll(brandListTask, typeListTask, itemListTask); - var brands = brandListTask.Result; - var types = typeListTask.Result; - var items = itemListTask.Result.CatalogItems; - foreach (var item in items) - { - item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name; - item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name; - } - return items; + var brandListTask = _brandService.List(); + var typeListTask = _typeService.List(); + var itemListTask = _httpService.HttpGet($"catalog-items"); + await Task.WhenAll(brandListTask, typeListTask, itemListTask); + var brands = brandListTask.Result; + var types = typeListTask.Result; + var items = itemListTask.Result.CatalogItems; + foreach (var item in items) + { + item.CatalogBrand = brands.FirstOrDefault(b => b.Id == item.CatalogBrandId)?.Name; + item.CatalogType = types.FirstOrDefault(t => t.Id == item.CatalogTypeId)?.Name; } + return items; } } diff --git a/src/BlazorAdmin/Services/CatalogLookupDataService.cs b/src/BlazorAdmin/Services/CatalogLookupDataService.cs index bd07459e2..4927d6fd3 100644 --- a/src/BlazorAdmin/Services/CatalogLookupDataService.cs +++ b/src/BlazorAdmin/Services/CatalogLookupDataService.cs @@ -1,43 +1,42 @@ -using BlazorShared; -using BlazorShared.Attributes; -using BlazorShared.Interfaces; -using BlazorShared.Models; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Reflection; using System.Threading.Tasks; +using BlazorShared; +using BlazorShared.Attributes; +using BlazorShared.Interfaces; +using BlazorShared.Models; +using Microsoft.Extensions.Logging; + +namespace BlazorAdmin.Services; -namespace BlazorAdmin.Services +public class CatalogLookupDataService + : ICatalogLookupDataService + where TLookupData : LookupData + where TReponse : ILookupDataResponse { - public class CatalogLookupDataService - : ICatalogLookupDataService - where TLookupData : LookupData - where TReponse : ILookupDataResponse - { - private readonly HttpClient _httpClient; - private readonly ILogger> _logger; - private readonly string _apiUrl; + private readonly HttpClient _httpClient; + private readonly ILogger> _logger; + private readonly string _apiUrl; - public CatalogLookupDataService(HttpClient httpClient, - BaseUrlConfiguration baseUrlConfiguration, - ILogger> logger) - { - _httpClient = httpClient; - _logger = logger; - _apiUrl = baseUrlConfiguration.ApiBase; - } + public CatalogLookupDataService(HttpClient httpClient, + BaseUrlConfiguration baseUrlConfiguration, + ILogger> logger) + { + _httpClient = httpClient; + _logger = logger; + _apiUrl = baseUrlConfiguration.ApiBase; + } - public async Task> List() - { - var endpointName = typeof(TLookupData).GetCustomAttribute().Name; - _logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}"); + public async Task> List() + { + var endpointName = typeof(TLookupData).GetCustomAttribute().Name; + _logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}"); - var response = await _httpClient.GetFromJsonAsync($"{_apiUrl}{endpointName}"); - return response.List; - } + var response = await _httpClient.GetFromJsonAsync($"{_apiUrl}{endpointName}"); + return response.List; } } diff --git a/src/BlazorAdmin/Services/HttpService.cs b/src/BlazorAdmin/Services/HttpService.cs index 67cac1eef..574992126 100644 --- a/src/BlazorAdmin/Services/HttpService.cs +++ b/src/BlazorAdmin/Services/HttpService.cs @@ -1,96 +1,95 @@ -using BlazorShared; -using BlazorShared.Models; -using System.Net.Http; +using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; +using BlazorShared; +using BlazorShared.Models; + +namespace BlazorAdmin.Services; -namespace BlazorAdmin.Services +public class HttpService { - public class HttpService - { - private readonly HttpClient _httpClient; - private readonly ToastService _toastService; - private readonly string _apiUrl; + private readonly HttpClient _httpClient; + private readonly ToastService _toastService; + private readonly string _apiUrl; + + public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration, ToastService toastService) + { + _httpClient = httpClient; + _toastService = toastService; + _apiUrl = baseUrlConfiguration.ApiBase; + } - public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration, ToastService toastService) + public async Task HttpGet(string uri) + where T : class + { + var result = await _httpClient.GetAsync($"{_apiUrl}{uri}"); + if (!result.IsSuccessStatusCode) { - _httpClient = httpClient; - _toastService = toastService; - _apiUrl = baseUrlConfiguration.ApiBase; + return null; } - public async Task HttpGet(string uri) - where T : class - { - var result = await _httpClient.GetAsync($"{_apiUrl}{uri}"); - if (!result.IsSuccessStatusCode) - { - return null; - } + return await FromHttpResponseMessage(result); + } - return await FromHttpResponseMessage(result); + public async Task HttpDelete(string uri, int id) + where T : class + { + var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}/{id}"); + if (!result.IsSuccessStatusCode) + { + return null; } - public async Task HttpDelete(string uri, int id) - where T : class - { - var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}/{id}"); - if (!result.IsSuccessStatusCode) - { - return null; - } + return await FromHttpResponseMessage(result); + } - return await FromHttpResponseMessage(result); - } + public async Task HttpPost(string uri, object dataToSend) + where T : class + { + var content = ToJson(dataToSend); - public async Task HttpPost(string uri, object dataToSend) - where T : class + var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content); + if (!result.IsSuccessStatusCode) { - var content = ToJson(dataToSend); - - var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content); - if (!result.IsSuccessStatusCode) + var exception = JsonSerializer.Deserialize(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions { - var exception = JsonSerializer.Deserialize(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - _toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error); - - return null; - } + PropertyNameCaseInsensitive = true + }); + _toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error); - return await FromHttpResponseMessage(result); + return null; } - public async Task HttpPut(string uri, object dataToSend) - where T : class - { - var content = ToJson(dataToSend); - - var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content); - if (!result.IsSuccessStatusCode) - { - _toastService.ShowToast("Error", ToastLevel.Error); - return null; - } + return await FromHttpResponseMessage(result); + } - return await FromHttpResponseMessage(result); - } + public async Task HttpPut(string uri, object dataToSend) + where T : class + { + var content = ToJson(dataToSend); - private StringContent ToJson(object obj) + var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content); + if (!result.IsSuccessStatusCode) { - return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json"); + _toastService.ShowToast("Error", ToastLevel.Error); + return null; } - private async Task FromHttpResponseMessage(HttpResponseMessage result) + return await FromHttpResponseMessage(result); + } + + private StringContent ToJson(object obj) + { + return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json"); + } + + private async Task FromHttpResponseMessage(HttpResponseMessage result) + { + return JsonSerializer.Deserialize(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions { - return JsonSerializer.Deserialize(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - } + PropertyNameCaseInsensitive = true + }); } } diff --git a/src/BlazorAdmin/Services/ToastService.cs b/src/BlazorAdmin/Services/ToastService.cs index fe503f239..ec868ad45 100644 --- a/src/BlazorAdmin/Services/ToastService.cs +++ b/src/BlazorAdmin/Services/ToastService.cs @@ -1,55 +1,54 @@ using System; using System.Timers; -namespace BlazorAdmin.Services +namespace BlazorAdmin.Services; + +public enum ToastLevel +{ + Info, + Success, + Warning, + Error +} + +public class ToastService : IDisposable { - public enum ToastLevel + public event Action OnShow; + public event Action OnHide; + private Timer Countdown; + public void ShowToast(string message, ToastLevel level) { - Info, - Success, - Warning, - Error + OnShow?.Invoke(message, level); + StartCountdown(); } - - public class ToastService : IDisposable + private void StartCountdown() { - public event Action OnShow; - public event Action OnHide; - private Timer Countdown; - public void ShowToast(string message, ToastLevel level) - { - OnShow?.Invoke(message, level); - StartCountdown(); - } - private void StartCountdown() + SetCountdown(); + if (Countdown.Enabled) { - SetCountdown(); - if (Countdown.Enabled) - { - Countdown.Stop(); - Countdown.Start(); - } - else - { - Countdown.Start(); - } + Countdown.Stop(); + Countdown.Start(); } - private void SetCountdown() + else { - if (Countdown == null) - { - Countdown = new Timer(3000); - Countdown.Elapsed += HideToast; - Countdown.AutoReset = false; - } + Countdown.Start(); } - private void HideToast(object source, ElapsedEventArgs args) - { - OnHide?.Invoke(); - } - public void Dispose() + } + private void SetCountdown() + { + if (Countdown == null) { - Countdown?.Dispose(); + Countdown = new Timer(3000); + Countdown.Elapsed += HideToast; + Countdown.AutoReset = false; } } + private void HideToast(object source, ElapsedEventArgs args) + { + OnHide?.Invoke(); + } + public void Dispose() + { + Countdown?.Dispose(); + } } diff --git a/src/BlazorAdmin/ServicesConfiguration.cs b/src/BlazorAdmin/ServicesConfiguration.cs index e95b183fd..a165b15c6 100644 --- a/src/BlazorAdmin/ServicesConfiguration.cs +++ b/src/BlazorAdmin/ServicesConfiguration.cs @@ -3,20 +3,19 @@ using BlazorShared.Models; using Microsoft.Extensions.DependencyInjection; -namespace BlazorAdmin +namespace BlazorAdmin; + +public static class ServicesConfiguration { - public static class ServicesConfiguration + public static IServiceCollection AddBlazorServices(this IServiceCollection services) { - public static IServiceCollection AddBlazorServices(this IServiceCollection services) - { - services.AddScoped, CachedCatalogLookupDataServiceDecorator>(); - services.AddScoped>(); - services.AddScoped, CachedCatalogLookupDataServiceDecorator>(); - services.AddScoped>(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped, CachedCatalogLookupDataServiceDecorator>(); + services.AddScoped>(); + services.AddScoped, CachedCatalogLookupDataServiceDecorator>(); + services.AddScoped>(); + services.AddScoped(); + services.AddScoped(); - return services; - } + return services; } } diff --git a/src/BlazorAdmin/Shared/CustomInputSelect.cs b/src/BlazorAdmin/Shared/CustomInputSelect.cs index 238a79a9c..eb39331e5 100644 --- a/src/BlazorAdmin/Shared/CustomInputSelect.cs +++ b/src/BlazorAdmin/Shared/CustomInputSelect.cs @@ -1,38 +1,37 @@ using Microsoft.AspNetCore.Components.Forms; -namespace BlazorAdmin.Shared +namespace BlazorAdmin.Shared; + +/// +/// This is needed until 5.0 ships with native support +/// https://www.pragimtech.com/blog/blazor/inputselect-does-not-support-system.int32/ +/// +/// +public class CustomInputSelect : InputSelect { - /// - /// This is needed until 5.0 ships with native support - /// https://www.pragimtech.com/blog/blazor/inputselect-does-not-support-system.int32/ - /// - /// - public class CustomInputSelect : InputSelect + protected override bool TryParseValueFromString(string value, out TValue result, + out string validationErrorMessage) { - protected override bool TryParseValueFromString(string value, out TValue result, - out string validationErrorMessage) + if (typeof(TValue) == typeof(int)) { - if (typeof(TValue) == typeof(int)) + if (int.TryParse(value, out var resultInt)) { - if (int.TryParse(value, out var resultInt)) - { - result = (TValue)(object)resultInt; - validationErrorMessage = null; - return true; - } - else - { - result = default; - validationErrorMessage = - $"The selected value {value} is not a valid number."; - return false; - } + result = (TValue)(object)resultInt; + validationErrorMessage = null; + return true; } else { - return base.TryParseValueFromString(value, out result, - out validationErrorMessage); + result = default; + validationErrorMessage = + $"The selected value {value} is not a valid number."; + return false; } } + else + { + return base.TryParseValueFromString(value, out result, + out validationErrorMessage); + } } } diff --git a/src/BlazorShared/Attributes/EndpointAttribute.cs b/src/BlazorShared/Attributes/EndpointAttribute.cs index ae0207bf0..204058410 100644 --- a/src/BlazorShared/Attributes/EndpointAttribute.cs +++ b/src/BlazorShared/Attributes/EndpointAttribute.cs @@ -1,9 +1,8 @@ using System; -namespace BlazorShared.Attributes +namespace BlazorShared.Attributes; + +public class EndpointAttribute : Attribute { - public class EndpointAttribute : Attribute - { - public string Name { get; set; } - } + public string Name { get; set; } } diff --git a/src/BlazorShared/Authorization/ClaimValue.cs b/src/BlazorShared/Authorization/ClaimValue.cs index 963da2270..a035d76b8 100644 --- a/src/BlazorShared/Authorization/ClaimValue.cs +++ b/src/BlazorShared/Authorization/ClaimValue.cs @@ -1,18 +1,17 @@ -namespace BlazorShared.Authorization +namespace BlazorShared.Authorization; + +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; } } diff --git a/src/BlazorShared/Authorization/Constants.cs b/src/BlazorShared/Authorization/Constants.cs index 4ddc12120..c89213786 100644 --- a/src/BlazorShared/Authorization/Constants.cs +++ b/src/BlazorShared/Authorization/Constants.cs @@ -1,10 +1,9 @@ -namespace BlazorShared.Authorization +namespace BlazorShared.Authorization; + +public static class Constants { - public static class Constants + public static class Roles { - public static class Roles - { - public const string ADMINISTRATORS = "Administrators"; - } + public const string ADMINISTRATORS = "Administrators"; } } diff --git a/src/BlazorShared/Authorization/UserInfo.cs b/src/BlazorShared/Authorization/UserInfo.cs index 9ae24b86b..9dd56d7bb 100644 --- a/src/BlazorShared/Authorization/UserInfo.cs +++ b/src/BlazorShared/Authorization/UserInfo.cs @@ -1,14 +1,13 @@ using System.Collections.Generic; -namespace BlazorShared.Authorization +namespace BlazorShared.Authorization; + +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 string Token { get; set; } - public IEnumerable 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 string Token { get; set; } + public IEnumerable Claims { get; set; } } diff --git a/src/BlazorShared/BaseUrlConfiguration.cs b/src/BlazorShared/BaseUrlConfiguration.cs index 419735fe8..af0d192ec 100644 --- a/src/BlazorShared/BaseUrlConfiguration.cs +++ b/src/BlazorShared/BaseUrlConfiguration.cs @@ -1,10 +1,9 @@ -namespace BlazorShared +namespace BlazorShared; + +public class BaseUrlConfiguration { - public class BaseUrlConfiguration - { - public const string CONFIG_NAME = "baseUrls"; + public const string CONFIG_NAME = "baseUrls"; - public string ApiBase { get; set; } - public string WebBase { get; set; } - } + public string ApiBase { get; set; } + public string WebBase { get; set; } } diff --git a/src/BlazorShared/BlazorShared.csproj b/src/BlazorShared/BlazorShared.csproj index 7a29d753d..8f5269e83 100644 --- a/src/BlazorShared/BlazorShared.csproj +++ b/src/BlazorShared/BlazorShared.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 BlazorShared BlazorShared diff --git a/src/BlazorShared/Interfaces/ICatalogItemService.cs b/src/BlazorShared/Interfaces/ICatalogItemService.cs index 3b277d3ef..7fd03301f 100644 --- a/src/BlazorShared/Interfaces/ICatalogItemService.cs +++ b/src/BlazorShared/Interfaces/ICatalogItemService.cs @@ -2,15 +2,14 @@ using System.Threading.Tasks; using BlazorShared.Models; -namespace BlazorShared.Interfaces +namespace BlazorShared.Interfaces; + +public interface ICatalogItemService { - public interface ICatalogItemService - { - Task Create(CreateCatalogItemRequest catalogItem); - Task Edit(CatalogItem catalogItem); - Task Delete(int id); - Task GetById(int id); - Task> ListPaged(int pageSize); - Task> List(); - } + Task Create(CreateCatalogItemRequest catalogItem); + Task Edit(CatalogItem catalogItem); + Task Delete(int id); + Task GetById(int id); + Task> ListPaged(int pageSize); + Task> List(); } diff --git a/src/BlazorShared/Interfaces/ICatalogLookupDataService.cs b/src/BlazorShared/Interfaces/ICatalogLookupDataService.cs index 00106f46a..ed6a35987 100644 --- a/src/BlazorShared/Interfaces/ICatalogLookupDataService.cs +++ b/src/BlazorShared/Interfaces/ICatalogLookupDataService.cs @@ -1,11 +1,10 @@ -using BlazorShared.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using BlazorShared.Models; -namespace BlazorShared.Interfaces +namespace BlazorShared.Interfaces; + +public interface ICatalogLookupDataService where TLookupData : LookupData { - public interface ICatalogLookupDataService where TLookupData : LookupData - { - Task> List(); - } + Task> List(); } diff --git a/src/BlazorShared/Interfaces/ILookupDataResponse.cs b/src/BlazorShared/Interfaces/ILookupDataResponse.cs index 83e95c8a4..8d92aca1c 100644 --- a/src/BlazorShared/Interfaces/ILookupDataResponse.cs +++ b/src/BlazorShared/Interfaces/ILookupDataResponse.cs @@ -1,10 +1,9 @@ -using BlazorShared.Models; -using System.Collections.Generic; +using System.Collections.Generic; +using BlazorShared.Models; -namespace BlazorShared.Interfaces +namespace BlazorShared.Interfaces; + +public interface ILookupDataResponse where TLookupData : LookupData { - public interface ILookupDataResponse where TLookupData : LookupData - { - List List { get; set; } - } + List List { get; set; } } diff --git a/src/BlazorShared/Models/CatalogBrand.cs b/src/BlazorShared/Models/CatalogBrand.cs index 5335ae82b..55bceb256 100644 --- a/src/BlazorShared/Models/CatalogBrand.cs +++ b/src/BlazorShared/Models/CatalogBrand.cs @@ -1,9 +1,8 @@ using BlazorShared.Attributes; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +[Endpoint(Name = "catalog-brands")] +public class CatalogBrand : LookupData { - [Endpoint(Name = "catalog-brands")] - public class CatalogBrand : LookupData - { - } } diff --git a/src/BlazorShared/Models/CatalogBrandResponse.cs b/src/BlazorShared/Models/CatalogBrandResponse.cs index 80cf0d3bd..c49171b12 100644 --- a/src/BlazorShared/Models/CatalogBrandResponse.cs +++ b/src/BlazorShared/Models/CatalogBrandResponse.cs @@ -1,12 +1,11 @@ -using BlazorShared.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.Json.Serialization; +using BlazorShared.Interfaces; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class CatalogBrandResponse : ILookupDataResponse { - public class CatalogBrandResponse : ILookupDataResponse - { - [JsonPropertyName("CatalogBrands")] - public List List { get; set; } = new List(); - } + [JsonPropertyName("CatalogBrands")] + public List List { get; set; } = new List(); } diff --git a/src/BlazorShared/Models/CatalogItem.cs b/src/BlazorShared/Models/CatalogItem.cs index 60da65a46..efc3bd1b4 100644 --- a/src/BlazorShared/Models/CatalogItem.cs +++ b/src/BlazorShared/Models/CatalogItem.cs @@ -4,85 +4,84 @@ using System.Threading.Tasks; using BlazorInputFile; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class CatalogItem { - public class CatalogItem - { - public int Id { get; set; } + public int Id { get; set; } - public int CatalogTypeId { get; set; } - public string CatalogType { get; set; } = "NotSet"; + public int CatalogTypeId { get; set; } + public string CatalogType { get; set; } = "NotSet"; - public int CatalogBrandId { get; set; } - public string CatalogBrand { get; set; } = "NotSet"; + public int CatalogBrandId { get; set; } + public string CatalogBrand { get; set; } = "NotSet"; - [Required(ErrorMessage = "The Name field is required")] - public string Name { get; set; } + [Required(ErrorMessage = "The Name field is required")] + public string Name { get; set; } - [Required(ErrorMessage = "The Description field is required")] - public string Description { get; set; } + [Required(ErrorMessage = "The Description field is required")] + public string Description { get; set; } - // decimal(18,2) - [RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")] - [Range(0.01, 1000)] - [DataType(DataType.Currency)] - public decimal Price { get; set; } + // decimal(18,2) + [RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")] + [Range(0.01, 1000)] + [DataType(DataType.Currency)] + public decimal Price { get; set; } - public string PictureUri { get; set; } - public string PictureBase64 { get; set; } - public string PictureName { get; set; } + public string PictureUri { get; set; } + public string PictureBase64 { get; set; } + public string PictureName { get; set; } - private const int ImageMaximumBytes = 512000; + private const int ImageMaximumBytes = 512000; - public static string IsValidImage(string pictureName, string pictureBase64) + public static string IsValidImage(string pictureName, string pictureBase64) + { + if (string.IsNullOrEmpty(pictureBase64)) { - if (string.IsNullOrEmpty(pictureBase64)) - { - return "File not found!"; - } - var fileData = Convert.FromBase64String(pictureBase64); - - if (fileData.Length <= 0) - { - return "File length is 0!"; - } + return "File not found!"; + } + var fileData = Convert.FromBase64String(pictureBase64); - if (fileData.Length > ImageMaximumBytes) - { - return "Maximum length is 512KB"; - } + if (fileData.Length <= 0) + { + return "File length is 0!"; + } - if (!IsExtensionValid(pictureName)) - { - return "File is not image"; - } + if (fileData.Length > ImageMaximumBytes) + { + return "Maximum length is 512KB"; + } - return null; + if (!IsExtensionValid(pictureName)) + { + return "File is not image"; } - public static async Task DataToBase64(IFileListEntry fileItem) + return null; + } + + public static async Task DataToBase64(IFileListEntry fileItem) + { + using (var reader = new StreamReader(fileItem.Data)) { - using ( var reader = new StreamReader(fileItem.Data)) + using (var memStream = new MemoryStream()) { - using (var memStream = new MemoryStream()) - { - await reader.BaseStream.CopyToAsync(memStream); - var fileData = memStream.ToArray(); - var encodedBase64 = Convert.ToBase64String(fileData); - - return encodedBase64; - } + await reader.BaseStream.CopyToAsync(memStream); + var fileData = memStream.ToArray(); + var encodedBase64 = Convert.ToBase64String(fileData); + + return encodedBase64; } } + } - 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); } } diff --git a/src/BlazorShared/Models/CatalogType.cs b/src/BlazorShared/Models/CatalogType.cs index 6e2526d15..d2e5662cf 100644 --- a/src/BlazorShared/Models/CatalogType.cs +++ b/src/BlazorShared/Models/CatalogType.cs @@ -1,9 +1,8 @@ using BlazorShared.Attributes; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +[Endpoint(Name = "catalog-types")] +public class CatalogType : LookupData { - [Endpoint(Name = "catalog-types")] - public class CatalogType : LookupData - { - } } diff --git a/src/BlazorShared/Models/CatalogTypeResponse.cs b/src/BlazorShared/Models/CatalogTypeResponse.cs index 8c8110095..65ce23102 100644 --- a/src/BlazorShared/Models/CatalogTypeResponse.cs +++ b/src/BlazorShared/Models/CatalogTypeResponse.cs @@ -1,13 +1,12 @@ -using BlazorShared.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.Json.Serialization; +using BlazorShared.Interfaces; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class CatalogTypeResponse : ILookupDataResponse { - public class CatalogTypeResponse : ILookupDataResponse - { - [JsonPropertyName("CatalogTypes")] - public List List { get; set; } = new List(); - } + [JsonPropertyName("CatalogTypes")] + public List List { get; set; } = new List(); } diff --git a/src/BlazorShared/Models/CreateCatalogItemRequest.cs b/src/BlazorShared/Models/CreateCatalogItemRequest.cs index 0894109ad..2da820186 100644 --- a/src/BlazorShared/Models/CreateCatalogItemRequest.cs +++ b/src/BlazorShared/Models/CreateCatalogItemRequest.cs @@ -1,28 +1,27 @@ using System.ComponentModel.DataAnnotations; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class CreateCatalogItemRequest { - public class CreateCatalogItemRequest - { - public int CatalogTypeId { get; set; } + public int CatalogTypeId { get; set; } - public int CatalogBrandId { get; set; } + public int CatalogBrandId { get; set; } - [Required(ErrorMessage = "The Name field is required")] - public string Name { get; set; } = string.Empty; + [Required(ErrorMessage = "The Name field is required")] + public string Name { get; set; } = string.Empty; - [Required(ErrorMessage = "The Description field is required")] - public string Description { get; set; } = string.Empty; + [Required(ErrorMessage = "The Description field is required")] + public string Description { get; set; } = string.Empty; - // decimal(18,2) - [RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")] - [Range(0.01, 1000)] - [DataType(DataType.Currency)] - public decimal Price { get; set; } = 0; + // decimal(18,2) + [RegularExpression(@"^\d+(\.\d{0,2})*$", ErrorMessage = "The field Price must be a positive number with maximum two decimals.")] + [Range(0.01, 1000)] + [DataType(DataType.Currency)] + public decimal Price { get; set; } = 0; - public string PictureUri { get; set; } = string.Empty; - public string PictureBase64 { get; set; } = string.Empty; - public string PictureName { get; set; } = string.Empty; + public string PictureUri { get; set; } = string.Empty; + public string PictureBase64 { get; set; } = string.Empty; + public string PictureName { get; set; } = string.Empty; - } } diff --git a/src/BlazorShared/Models/CreateCatalogItemResponse.cs b/src/BlazorShared/Models/CreateCatalogItemResponse.cs index e51d3ffbe..22076e8da 100644 --- a/src/BlazorShared/Models/CreateCatalogItemResponse.cs +++ b/src/BlazorShared/Models/CreateCatalogItemResponse.cs @@ -1,7 +1,6 @@ -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class CreateCatalogItemResponse { - public class CreateCatalogItemResponse - { - public CatalogItem CatalogItem { get; set; } = new CatalogItem(); - } + public CatalogItem CatalogItem { get; set; } = new CatalogItem(); } diff --git a/src/BlazorShared/Models/DeleteCatalogItemResponse.cs b/src/BlazorShared/Models/DeleteCatalogItemResponse.cs index 3cd1e2264..46a6016db 100644 --- a/src/BlazorShared/Models/DeleteCatalogItemResponse.cs +++ b/src/BlazorShared/Models/DeleteCatalogItemResponse.cs @@ -1,7 +1,6 @@ -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class DeleteCatalogItemResponse { - public class DeleteCatalogItemResponse - { - public string Status { get; set; } = "Deleted"; - } + public string Status { get; set; } = "Deleted"; } diff --git a/src/BlazorShared/Models/EditCatalogItemResponse.cs b/src/BlazorShared/Models/EditCatalogItemResponse.cs index ed690d6f6..663f9af88 100644 --- a/src/BlazorShared/Models/EditCatalogItemResponse.cs +++ b/src/BlazorShared/Models/EditCatalogItemResponse.cs @@ -1,7 +1,6 @@ -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class EditCatalogItemResult { - public class EditCatalogItemResult - { - public CatalogItem CatalogItem { get; set; } = new CatalogItem(); - } + public CatalogItem CatalogItem { get; set; } = new CatalogItem(); } diff --git a/src/BlazorShared/Models/ErrorDetails.cs b/src/BlazorShared/Models/ErrorDetails.cs index 661612923..389c039a3 100644 --- a/src/BlazorShared/Models/ErrorDetails.cs +++ b/src/BlazorShared/Models/ErrorDetails.cs @@ -1,14 +1,13 @@ using System.Text.Json; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class ErrorDetails { - public class ErrorDetails + public int StatusCode { get; set; } + public string Message { get; set; } + public override string ToString() { - public int StatusCode { get; set; } - public string Message { get; set; } - public override string ToString() - { - return JsonSerializer.Serialize(this); - } + return JsonSerializer.Serialize(this); } } diff --git a/src/BlazorShared/Models/LookupData.cs b/src/BlazorShared/Models/LookupData.cs index d54f94f08..2e227a35b 100644 --- a/src/BlazorShared/Models/LookupData.cs +++ b/src/BlazorShared/Models/LookupData.cs @@ -1,8 +1,7 @@ -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public abstract class LookupData { - public abstract class LookupData - { - public int Id { get; set; } - public string Name { get; set; } - } + public int Id { get; set; } + public string Name { get; set; } } diff --git a/src/BlazorShared/Models/PagedCatalogItemResponse.cs b/src/BlazorShared/Models/PagedCatalogItemResponse.cs index 3395b1c89..57fc96dbe 100644 --- a/src/BlazorShared/Models/PagedCatalogItemResponse.cs +++ b/src/BlazorShared/Models/PagedCatalogItemResponse.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace BlazorShared.Models +namespace BlazorShared.Models; + +public class PagedCatalogItemResponse { - public class PagedCatalogItemResponse - { - public List CatalogItems { get; set; } = new List(); - public int PageCount { get; set; } = 0; - } + public List CatalogItems { get; set; } = new List(); + public int PageCount { get; set; } = 0; } diff --git a/src/Infrastructure/Data/BasketQueryService.cs b/src/Infrastructure/Data/BasketQueryService.cs index 136c5cbdd..4d392fec6 100644 --- a/src/Infrastructure/Data/BasketQueryService.cs +++ b/src/Infrastructure/Data/BasketQueryService.cs @@ -1,27 +1,26 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System.Linq; +using System.Linq; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.Infrastructure.Data +namespace Microsoft.eShopWeb.Infrastructure.Data; + +public class BasketQueryService : IBasketQueryService { - public class BasketQueryService : IBasketQueryService - { - private readonly CatalogContext _dbContext; + private readonly CatalogContext _dbContext; - public BasketQueryService(CatalogContext dbContext) - { - _dbContext = dbContext; - } + public BasketQueryService(CatalogContext dbContext) + { + _dbContext = dbContext; + } - public async Task CountTotalBasketItems(string username) - { - var totalItems = await _dbContext.Baskets - .Where(basket => basket.BuyerId == username) - .SelectMany(item => item.Items) - .SumAsync(sum => sum.Quantity); + public async Task CountTotalBasketItems(string username) + { + var totalItems = await _dbContext.Baskets + .Where(basket => basket.BuyerId == username) + .SelectMany(item => item.Items) + .SumAsync(sum => sum.Quantity); - return totalItems; - } + return totalItems; } } diff --git a/src/Infrastructure/Data/CatalogContext.cs b/src/Infrastructure/Data/CatalogContext.cs index 03cda588c..f9f340a6d 100644 --- a/src/Infrastructure/Data/CatalogContext.cs +++ b/src/Infrastructure/Data/CatalogContext.cs @@ -1,30 +1,28 @@ -using Microsoft.EntityFrameworkCore; +using System.Reflection; +using Microsoft.EntityFrameworkCore; using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -using System.Reflection; -namespace Microsoft.eShopWeb.Infrastructure.Data -{ +namespace Microsoft.eShopWeb.Infrastructure.Data; - public class CatalogContext : DbContext +public class CatalogContext : DbContext +{ + public CatalogContext(DbContextOptions options) : base(options) { - public CatalogContext(DbContextOptions options) : base(options) - { - } + } - public DbSet Baskets { get; set; } - public DbSet CatalogItems { get; set; } - public DbSet CatalogBrands { get; set; } - public DbSet CatalogTypes { get; set; } - public DbSet Orders { get; set; } - public DbSet OrderItems { get; set; } - public DbSet BasketItems { get; set; } + public DbSet Baskets { get; set; } + public DbSet CatalogItems { get; set; } + public DbSet CatalogBrands { get; set; } + public DbSet CatalogTypes { get; set; } + public DbSet Orders { get; set; } + public DbSet OrderItems { get; set; } + public DbSet BasketItems { get; set; } - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); - } + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); } } diff --git a/src/Infrastructure/Data/CatalogContextSeed.cs b/src/Infrastructure/Data/CatalogContextSeed.cs index f36909435..7fbd88193 100644 --- a/src/Infrastructure/Data/CatalogContextSeed.cs +++ b/src/Infrastructure/Data/CatalogContextSeed.cs @@ -1,64 +1,64 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopWeb.ApplicationCore.Entities; -using Microsoft.Extensions.Logging; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopWeb.ApplicationCore.Entities; +using Microsoft.Extensions.Logging; -namespace Microsoft.eShopWeb.Infrastructure.Data +namespace Microsoft.eShopWeb.Infrastructure.Data; + +public class CatalogContextSeed { - public class CatalogContextSeed + public static async Task SeedAsync(CatalogContext catalogContext, + ILoggerFactory loggerFactory, int retry = 0) { - public static async Task SeedAsync(CatalogContext catalogContext, - ILoggerFactory loggerFactory, int retry = 0) + var retryForAvailability = retry; + try { - var retryForAvailability = retry; - try + if (catalogContext.Database.IsSqlServer()) { - if (catalogContext.Database.IsSqlServer()) - { - catalogContext.Database.Migrate(); - } - - if (!await catalogContext.CatalogBrands.AnyAsync()) - { - await catalogContext.CatalogBrands.AddRangeAsync( - GetPreconfiguredCatalogBrands()); - - await catalogContext.SaveChangesAsync(); - } + catalogContext.Database.Migrate(); + } - if (!await catalogContext.CatalogTypes.AnyAsync()) - { - await catalogContext.CatalogTypes.AddRangeAsync( - GetPreconfiguredCatalogTypes()); + if (!await catalogContext.CatalogBrands.AnyAsync()) + { + await catalogContext.CatalogBrands.AddRangeAsync( + GetPreconfiguredCatalogBrands()); - await catalogContext.SaveChangesAsync(); - } + await catalogContext.SaveChangesAsync(); + } - if (!await catalogContext.CatalogItems.AnyAsync()) - { - await catalogContext.CatalogItems.AddRangeAsync( - GetPreconfiguredItems()); + if (!await catalogContext.CatalogTypes.AnyAsync()) + { + await catalogContext.CatalogTypes.AddRangeAsync( + GetPreconfiguredCatalogTypes()); - await catalogContext.SaveChangesAsync(); - } + await catalogContext.SaveChangesAsync(); } - catch (Exception ex) + + if (!await catalogContext.CatalogItems.AnyAsync()) { - if (retryForAvailability >= 10) throw; + await catalogContext.CatalogItems.AddRangeAsync( + GetPreconfiguredItems()); - retryForAvailability++; - var log = loggerFactory.CreateLogger(); - log.LogError(ex.Message); - await SeedAsync(catalogContext, loggerFactory, retryForAvailability); - throw; + await catalogContext.SaveChangesAsync(); } } - - static IEnumerable GetPreconfiguredCatalogBrands() + catch (Exception ex) { - return new List + if (retryForAvailability >= 10) throw; + + retryForAvailability++; + var log = loggerFactory.CreateLogger(); + log.LogError(ex.Message); + await SeedAsync(catalogContext, loggerFactory, retryForAvailability); + throw; + } + } + + static IEnumerable GetPreconfiguredCatalogBrands() + { + return new List { new("Azure"), new(".NET"), @@ -66,22 +66,22 @@ static IEnumerable GetPreconfiguredCatalogBrands() new("SQL Server"), new("Other") }; - } + } - static IEnumerable GetPreconfiguredCatalogTypes() - { - return new List + static IEnumerable GetPreconfiguredCatalogTypes() + { + return new List { new("Mug"), new("T-Shirt"), new("Sheet"), new("USB Memory Stick") }; - } + } - static IEnumerable GetPreconfiguredItems() - { - return new List + static IEnumerable GetPreconfiguredItems() + { + return new List { new(2,2, ".NET Bot Black Sweatshirt", ".NET Bot Black Sweatshirt", 19.5M, "http://catalogbaseurltobereplaced/images/products/1.png"), new(1,2, ".NET Black & White Mug", ".NET Black & White Mug", 8.50M, "http://catalogbaseurltobereplaced/images/products/2.png"), @@ -96,6 +96,5 @@ static IEnumerable GetPreconfiguredItems() new(3,2, "Cup Sheet", "Cup Sheet", 8.5M, "http://catalogbaseurltobereplaced/images/products/11.png"), new(2,5, "Prism White TShirt", "Prism White TShirt", 12, "http://catalogbaseurltobereplaced/images/products/12.png") }; - } } } diff --git a/src/Infrastructure/Data/Config/BasketConfiguration.cs b/src/Infrastructure/Data/Config/BasketConfiguration.cs index 258fc2720..d66fcf48b 100644 --- a/src/Infrastructure/Data/Config/BasketConfiguration.cs +++ b/src/Infrastructure/Data/Config/BasketConfiguration.cs @@ -2,18 +2,17 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; -namespace Microsoft.eShopWeb.Infrastructure.Data.Config +namespace Microsoft.eShopWeb.Infrastructure.Data.Config; + +public class BasketConfiguration : IEntityTypeConfiguration { - public class BasketConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - var navigation = builder.Metadata.FindNavigation(nameof(Basket.Items)); - navigation.SetPropertyAccessMode(PropertyAccessMode.Field); + var navigation = builder.Metadata.FindNavigation(nameof(Basket.Items)); + navigation.SetPropertyAccessMode(PropertyAccessMode.Field); - builder.Property(b => b.BuyerId) - .IsRequired() - .HasMaxLength(256); - } + builder.Property(b => b.BuyerId) + .IsRequired() + .HasMaxLength(256); } } diff --git a/src/Infrastructure/Data/Config/BasketItemConfiguration.cs b/src/Infrastructure/Data/Config/BasketItemConfiguration.cs index 7d9aba1bb..648cfa52c 100644 --- a/src/Infrastructure/Data/Config/BasketItemConfiguration.cs +++ b/src/Infrastructure/Data/Config/BasketItemConfiguration.cs @@ -2,15 +2,14 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; -namespace Microsoft.eShopWeb.Infrastructure.Data.Config +namespace Microsoft.eShopWeb.Infrastructure.Data.Config; + +public class BasketItemConfiguration : IEntityTypeConfiguration { - public class BasketItemConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - builder.Property(bi => bi.UnitPrice) - .IsRequired(true) - .HasColumnType("decimal(18,2)"); - } + builder.Property(bi => bi.UnitPrice) + .IsRequired(true) + .HasColumnType("decimal(18,2)"); } } diff --git a/src/Infrastructure/Data/Config/CatalogBrandConfiguration.cs b/src/Infrastructure/Data/Config/CatalogBrandConfiguration.cs index c571bb0ce..18cb1f53e 100644 --- a/src/Infrastructure/Data/Config/CatalogBrandConfiguration.cs +++ b/src/Infrastructure/Data/Config/CatalogBrandConfiguration.cs @@ -2,21 +2,20 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopWeb.ApplicationCore.Entities; -namespace Microsoft.eShopWeb.Infrastructure.Data.Config +namespace Microsoft.eShopWeb.Infrastructure.Data.Config; + +public class CatalogBrandConfiguration : IEntityTypeConfiguration { - public class CatalogBrandConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(ci => ci.Id); + builder.HasKey(ci => ci.Id); - builder.Property(ci => ci.Id) - .UseHiLo("catalog_brand_hilo") - .IsRequired(); + builder.Property(ci => ci.Id) + .UseHiLo("catalog_brand_hilo") + .IsRequired(); - builder.Property(cb => cb.Brand) - .IsRequired() - .HasMaxLength(100); - } + builder.Property(cb => cb.Brand) + .IsRequired() + .HasMaxLength(100); } } diff --git a/src/Infrastructure/Data/Config/CatalogItemConfiguration.cs b/src/Infrastructure/Data/Config/CatalogItemConfiguration.cs index 6a70a221d..708131ef6 100644 --- a/src/Infrastructure/Data/Config/CatalogItemConfiguration.cs +++ b/src/Infrastructure/Data/Config/CatalogItemConfiguration.cs @@ -2,36 +2,35 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopWeb.ApplicationCore.Entities; -namespace Microsoft.eShopWeb.Infrastructure.Data.Config +namespace Microsoft.eShopWeb.Infrastructure.Data.Config; + +public class CatalogItemConfiguration : IEntityTypeConfiguration { - public class CatalogItemConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("Catalog"); + builder.ToTable("Catalog"); - builder.Property(ci => ci.Id) - .UseHiLo("catalog_hilo") - .IsRequired(); + builder.Property(ci => ci.Id) + .UseHiLo("catalog_hilo") + .IsRequired(); - builder.Property(ci => ci.Name) - .IsRequired(true) - .HasMaxLength(50); + builder.Property(ci => ci.Name) + .IsRequired(true) + .HasMaxLength(50); - builder.Property(ci => ci.Price) - .IsRequired(true) - .HasColumnType("decimal(18,2)"); + builder.Property(ci => ci.Price) + .IsRequired(true) + .HasColumnType("decimal(18,2)"); - builder.Property(ci => ci.PictureUri) - .IsRequired(false); + builder.Property(ci => ci.PictureUri) + .IsRequired(false); - builder.HasOne(ci => ci.CatalogBrand) - .WithMany() - .HasForeignKey(ci => ci.CatalogBrandId); + builder.HasOne(ci => ci.CatalogBrand) + .WithMany() + .HasForeignKey(ci => ci.CatalogBrandId); - builder.HasOne(ci => ci.CatalogType) - .WithMany() - .HasForeignKey(ci => ci.CatalogTypeId); - } + builder.HasOne(ci => ci.CatalogType) + .WithMany() + .HasForeignKey(ci => ci.CatalogTypeId); } } diff --git a/src/Infrastructure/Data/Config/CatalogTypeConfiguration.cs b/src/Infrastructure/Data/Config/CatalogTypeConfiguration.cs index 5caa01c7c..62d28e5d4 100644 --- a/src/Infrastructure/Data/Config/CatalogTypeConfiguration.cs +++ b/src/Infrastructure/Data/Config/CatalogTypeConfiguration.cs @@ -2,21 +2,20 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopWeb.ApplicationCore.Entities; -namespace Microsoft.eShopWeb.Infrastructure.Data.Config +namespace Microsoft.eShopWeb.Infrastructure.Data.Config; + +public class CatalogTypeConfiguration : IEntityTypeConfiguration { - public class CatalogTypeConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(ci => ci.Id); + builder.HasKey(ci => ci.Id); - builder.Property(ci => ci.Id) - .UseHiLo("catalog_type_hilo") - .IsRequired(); + builder.Property(ci => ci.Id) + .UseHiLo("catalog_type_hilo") + .IsRequired(); - builder.Property(cb => cb.Type) - .IsRequired() - .HasMaxLength(100); - } + builder.Property(cb => cb.Type) + .IsRequired() + .HasMaxLength(100); } } diff --git a/src/Infrastructure/Data/Config/OrderConfiguration.cs b/src/Infrastructure/Data/Config/OrderConfiguration.cs index 598709b73..735e91407 100644 --- a/src/Infrastructure/Data/Config/OrderConfiguration.cs +++ b/src/Infrastructure/Data/Config/OrderConfiguration.cs @@ -2,43 +2,42 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.Infrastructure.Data.Config +namespace Microsoft.eShopWeb.Infrastructure.Data.Config; + +public class OrderConfiguration : IEntityTypeConfiguration { - public class OrderConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) + var navigation = builder.Metadata.FindNavigation(nameof(Order.OrderItems)); + + navigation.SetPropertyAccessMode(PropertyAccessMode.Field); + + builder.Property(b => b.BuyerId) + .IsRequired() + .HasMaxLength(256); + + builder.OwnsOne(o => o.ShipToAddress, a => { - var navigation = builder.Metadata.FindNavigation(nameof(Order.OrderItems)); - - navigation.SetPropertyAccessMode(PropertyAccessMode.Field); - - builder.Property(b => b.BuyerId) - .IsRequired() - .HasMaxLength(256); - - builder.OwnsOne(o => o.ShipToAddress, a => - { - a.WithOwner(); - - a.Property(a => a.ZipCode) - .HasMaxLength(18) - .IsRequired(); - - a.Property(a => a.Street) - .HasMaxLength(180) - .IsRequired(); - - a.Property(a => a.State) - .HasMaxLength(60); - - a.Property(a => a.Country) - .HasMaxLength(90) - .IsRequired(); - - a.Property(a => a.City) - .HasMaxLength(100) - .IsRequired(); - }); - } + a.WithOwner(); + + a.Property(a => a.ZipCode) + .HasMaxLength(18) + .IsRequired(); + + a.Property(a => a.Street) + .HasMaxLength(180) + .IsRequired(); + + a.Property(a => a.State) + .HasMaxLength(60); + + a.Property(a => a.Country) + .HasMaxLength(90) + .IsRequired(); + + a.Property(a => a.City) + .HasMaxLength(100) + .IsRequired(); + }); } } diff --git a/src/Infrastructure/Data/Config/OrderItemConfiguration.cs b/src/Infrastructure/Data/Config/OrderItemConfiguration.cs index 41a92e972..0280596e0 100644 --- a/src/Infrastructure/Data/Config/OrderItemConfiguration.cs +++ b/src/Infrastructure/Data/Config/OrderItemConfiguration.cs @@ -2,24 +2,23 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.Infrastructure.Data.Config +namespace Microsoft.eShopWeb.Infrastructure.Data.Config; + +public class OrderItemConfiguration : IEntityTypeConfiguration { - public class OrderItemConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) + builder.OwnsOne(i => i.ItemOrdered, io => { - builder.OwnsOne(i => i.ItemOrdered, io => - { - io.WithOwner(); + io.WithOwner(); - io.Property(cio => cio.ProductName) - .HasMaxLength(50) - .IsRequired(); - }); + io.Property(cio => cio.ProductName) + .HasMaxLength(50) + .IsRequired(); + }); - builder.Property(oi => oi.UnitPrice) - .IsRequired(true) - .HasColumnType("decimal(18,2)"); - } + builder.Property(oi => oi.UnitPrice) + .IsRequired(true) + .HasColumnType("decimal(18,2)"); } } diff --git a/src/Infrastructure/Data/EfRepository.cs b/src/Infrastructure/Data/EfRepository.cs index 0c027a731..a005750e7 100644 --- a/src/Infrastructure/Data/EfRepository.cs +++ b/src/Infrastructure/Data/EfRepository.cs @@ -1,12 +1,11 @@ using Ardalis.Specification.EntityFrameworkCore; using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.Infrastructure.Data +namespace Microsoft.eShopWeb.Infrastructure.Data; + +public class EfRepository : RepositoryBase, IReadRepository, IRepository where T : class, IAggregateRoot { - public class EfRepository : RepositoryBase, IReadRepository, IRepository where T : class, IAggregateRoot + public EfRepository(CatalogContext dbContext) : base(dbContext) { - public EfRepository(CatalogContext dbContext) : base(dbContext) - { - } } -} \ No newline at end of file +} diff --git a/src/Infrastructure/Data/FileItem.cs b/src/Infrastructure/Data/FileItem.cs index 3267d249d..0229b8863 100644 --- a/src/Infrastructure/Data/FileItem.cs +++ b/src/Infrastructure/Data/FileItem.cs @@ -1,12 +1,11 @@ -namespace Microsoft.eShopWeb.Infrastructure.Data +namespace Microsoft.eShopWeb.Infrastructure.Data; + +public class FileItem { - public class FileItem - { - public string FileName { get; set; } - public string Url { get; set; } - public long Size { get; set; } - public string Ext { get; set; } - public string Type { get; set; } - public string DataBase64 { get; set; } - } -} \ No newline at end of file + public string FileName { get; set; } + public string Url { get; set; } + public long Size { get; set; } + public string Ext { get; set; } + public string Type { get; set; } + public string DataBase64 { get; set; } +} diff --git a/src/Infrastructure/Data/Migrations/20201202111507_InitialModel.cs b/src/Infrastructure/Data/Migrations/20201202111507_InitialModel.cs index a850d109b..7878aef14 100644 --- a/src/Infrastructure/Data/Migrations/20201202111507_InitialModel.cs +++ b/src/Infrastructure/Data/Migrations/20201202111507_InitialModel.cs @@ -1,207 +1,206 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations +namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations; + +public partial class InitialModel : Migration { - public partial class InitialModel : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateSequence( + name: "catalog_brand_hilo", + incrementBy: 10); + + migrationBuilder.CreateSequence( + name: "catalog_hilo", + incrementBy: 10); + + migrationBuilder.CreateSequence( + name: "catalog_type_hilo", + incrementBy: 10); + + migrationBuilder.CreateTable( + name: "Baskets", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + BuyerId = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Baskets", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "CatalogBrands", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + Brand = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CatalogBrands", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "CatalogTypes", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + Type = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CatalogTypes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + BuyerId = table.Column(type: "nvarchar(max)", nullable: true), + OrderDate = table.Column(type: "datetimeoffset", nullable: false), + ShipToAddress_Street = table.Column(type: "nvarchar(180)", maxLength: 180, nullable: true), + ShipToAddress_City = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + ShipToAddress_State = table.Column(type: "nvarchar(60)", maxLength: 60, nullable: true), + ShipToAddress_Country = table.Column(type: "nvarchar(90)", maxLength: 90, nullable: true), + ShipToAddress_ZipCode = table.Column(type: "nvarchar(18)", maxLength: 18, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "BasketItems", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UnitPrice = table.Column(type: "decimal(18,2)", nullable: false), + Quantity = table.Column(type: "int", nullable: false), + CatalogItemId = table.Column(type: "int", nullable: false), + BasketId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_BasketItems", x => x.Id); + table.ForeignKey( + name: "FK_BasketItems_Baskets_BasketId", + column: x => x.BasketId, + principalTable: "Baskets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Catalog", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + Name = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true), + Price = table.Column(type: "decimal(18,2)", nullable: false), + PictureUri = table.Column(type: "nvarchar(max)", nullable: true), + CatalogTypeId = table.Column(type: "int", nullable: false), + CatalogBrandId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Catalog", x => x.Id); + table.ForeignKey( + name: "FK_Catalog_CatalogBrands_CatalogBrandId", + column: x => x.CatalogBrandId, + principalTable: "CatalogBrands", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Catalog_CatalogTypes_CatalogTypeId", + column: x => x.CatalogTypeId, + principalTable: "CatalogTypes", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OrderItems", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ItemOrdered_CatalogItemId = table.Column(type: "int", nullable: true), + ItemOrdered_ProductName = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + ItemOrdered_PictureUri = table.Column(type: "nvarchar(max)", nullable: true), + UnitPrice = table.Column(type: "decimal(18,2)", nullable: false), + Units = table.Column(type: "int", nullable: false), + OrderId = table.Column(type: "int", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderItems", x => x.Id); + table.ForeignKey( + name: "FK_OrderItems_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_BasketItems_BasketId", + table: "BasketItems", + column: "BasketId"); + + migrationBuilder.CreateIndex( + name: "IX_Catalog_CatalogBrandId", + table: "Catalog", + column: "CatalogBrandId"); + + migrationBuilder.CreateIndex( + name: "IX_Catalog_CatalogTypeId", + table: "Catalog", + column: "CatalogTypeId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItems_OrderId", + table: "OrderItems", + column: "OrderId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateSequence( - name: "catalog_brand_hilo", - incrementBy: 10); - - migrationBuilder.CreateSequence( - name: "catalog_hilo", - incrementBy: 10); - - migrationBuilder.CreateSequence( - name: "catalog_type_hilo", - incrementBy: 10); - - migrationBuilder.CreateTable( - name: "Baskets", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - BuyerId = table.Column(type: "nvarchar(40)", maxLength: 40, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Baskets", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "CatalogBrands", - columns: table => new - { - Id = table.Column(type: "int", nullable: false), - Brand = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CatalogBrands", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "CatalogTypes", - columns: table => new - { - Id = table.Column(type: "int", nullable: false), - Type = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CatalogTypes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Orders", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - BuyerId = table.Column(type: "nvarchar(max)", nullable: true), - OrderDate = table.Column(type: "datetimeoffset", nullable: false), - ShipToAddress_Street = table.Column(type: "nvarchar(180)", maxLength: 180, nullable: true), - ShipToAddress_City = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - ShipToAddress_State = table.Column(type: "nvarchar(60)", maxLength: 60, nullable: true), - ShipToAddress_Country = table.Column(type: "nvarchar(90)", maxLength: 90, nullable: true), - ShipToAddress_ZipCode = table.Column(type: "nvarchar(18)", maxLength: 18, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Orders", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "BasketItems", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UnitPrice = table.Column(type: "decimal(18,2)", nullable: false), - Quantity = table.Column(type: "int", nullable: false), - CatalogItemId = table.Column(type: "int", nullable: false), - BasketId = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BasketItems", x => x.Id); - table.ForeignKey( - name: "FK_BasketItems_Baskets_BasketId", - column: x => x.BasketId, - principalTable: "Baskets", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "Catalog", - columns: table => new - { - Id = table.Column(type: "int", nullable: false), - Name = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), - Description = table.Column(type: "nvarchar(max)", nullable: true), - Price = table.Column(type: "decimal(18,2)", nullable: false), - PictureUri = table.Column(type: "nvarchar(max)", nullable: true), - CatalogTypeId = table.Column(type: "int", nullable: false), - CatalogBrandId = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Catalog", x => x.Id); - table.ForeignKey( - name: "FK_Catalog_CatalogBrands_CatalogBrandId", - column: x => x.CatalogBrandId, - principalTable: "CatalogBrands", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_Catalog_CatalogTypes_CatalogTypeId", - column: x => x.CatalogTypeId, - principalTable: "CatalogTypes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "OrderItems", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - ItemOrdered_CatalogItemId = table.Column(type: "int", nullable: true), - ItemOrdered_ProductName = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - ItemOrdered_PictureUri = table.Column(type: "nvarchar(max)", nullable: true), - UnitPrice = table.Column(type: "decimal(18,2)", nullable: false), - Units = table.Column(type: "int", nullable: false), - OrderId = table.Column(type: "int", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OrderItems", x => x.Id); - table.ForeignKey( - name: "FK_OrderItems_Orders_OrderId", - column: x => x.OrderId, - principalTable: "Orders", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateIndex( - name: "IX_BasketItems_BasketId", - table: "BasketItems", - column: "BasketId"); - - migrationBuilder.CreateIndex( - name: "IX_Catalog_CatalogBrandId", - table: "Catalog", - column: "CatalogBrandId"); - - migrationBuilder.CreateIndex( - name: "IX_Catalog_CatalogTypeId", - table: "Catalog", - column: "CatalogTypeId"); - - migrationBuilder.CreateIndex( - name: "IX_OrderItems_OrderId", - table: "OrderItems", - column: "OrderId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "BasketItems"); - - migrationBuilder.DropTable( - name: "Catalog"); - - migrationBuilder.DropTable( - name: "OrderItems"); - - migrationBuilder.DropTable( - name: "Baskets"); - - migrationBuilder.DropTable( - name: "CatalogBrands"); - - migrationBuilder.DropTable( - name: "CatalogTypes"); - - migrationBuilder.DropTable( - name: "Orders"); - - migrationBuilder.DropSequence( - name: "catalog_brand_hilo"); - - migrationBuilder.DropSequence( - name: "catalog_hilo"); - - migrationBuilder.DropSequence( - name: "catalog_type_hilo"); - } + migrationBuilder.DropTable( + name: "BasketItems"); + + migrationBuilder.DropTable( + name: "Catalog"); + + migrationBuilder.DropTable( + name: "OrderItems"); + + migrationBuilder.DropTable( + name: "Baskets"); + + migrationBuilder.DropTable( + name: "CatalogBrands"); + + migrationBuilder.DropTable( + name: "CatalogTypes"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropSequence( + name: "catalog_brand_hilo"); + + migrationBuilder.DropSequence( + name: "catalog_hilo"); + + migrationBuilder.DropSequence( + name: "catalog_type_hilo"); } } diff --git a/src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.cs b/src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.cs index 21eb0bc88..7f15ed4ea 100644 --- a/src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.cs +++ b/src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.cs @@ -1,53 +1,52 @@ using Microsoft.EntityFrameworkCore.Migrations; -namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations +namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations; + +public partial class FixBuyerId : Migration { - public partial class FixBuyerId : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "BuyerId", - table: "Orders", - type: "nvarchar(256)", - maxLength: 256, - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); + migrationBuilder.AlterColumn( + name: "BuyerId", + table: "Orders", + type: "nvarchar(256)", + maxLength: 256, + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); - migrationBuilder.AlterColumn( - name: "BuyerId", - table: "Baskets", - type: "nvarchar(256)", - maxLength: 256, - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(40)", - oldMaxLength: 40); - } + migrationBuilder.AlterColumn( + name: "BuyerId", + table: "Baskets", + type: "nvarchar(256)", + maxLength: 256, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(40)", + oldMaxLength: 40); + } - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "BuyerId", - table: "Orders", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(256)", - oldMaxLength: 256); + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "BuyerId", + table: "Orders", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(256)", + oldMaxLength: 256); - migrationBuilder.AlterColumn( - name: "BuyerId", - table: "Baskets", - type: "nvarchar(40)", - maxLength: 40, - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(256)", - oldMaxLength: 256); - } + migrationBuilder.AlterColumn( + name: "BuyerId", + table: "Baskets", + type: "nvarchar(40)", + maxLength: 40, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(256)", + oldMaxLength: 256); } } diff --git a/src/Infrastructure/Identity/AppIdentityDbContext.cs b/src/Infrastructure/Identity/AppIdentityDbContext.cs index 3fa8f629c..c9507f11a 100644 --- a/src/Infrastructure/Identity/AppIdentityDbContext.cs +++ b/src/Infrastructure/Identity/AppIdentityDbContext.cs @@ -2,22 +2,20 @@ using Microsoft.EntityFrameworkCore; -namespace Microsoft.eShopWeb.Infrastructure.Identity +namespace Microsoft.eShopWeb.Infrastructure.Identity; + +public class AppIdentityDbContext : IdentityDbContext { - public class AppIdentityDbContext : IdentityDbContext + public AppIdentityDbContext(DbContextOptions options) + : base(options) { - public AppIdentityDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - // Customize the ASP.NET Identity model and override the defaults if needed. - // For example, you can rename the ASP.NET Identity table names and more. - // Add your customizations after calling base.OnModelCreating(builder); - } } + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + // Customize the ASP.NET Identity model and override the defaults if needed. + // For example, you can rename the ASP.NET Identity table names and more. + // Add your customizations after calling base.OnModelCreating(builder); + } } diff --git a/src/Infrastructure/Identity/AppIdentityDbContextSeed.cs b/src/Infrastructure/Identity/AppIdentityDbContextSeed.cs index e30edcd00..a3315deae 100644 --- a/src/Infrastructure/Identity/AppIdentityDbContextSeed.cs +++ b/src/Infrastructure/Identity/AppIdentityDbContextSeed.cs @@ -1,23 +1,22 @@ -using Microsoft.AspNetCore.Identity; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.eShopWeb.ApplicationCore.Constants; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Infrastructure.Identity +namespace Microsoft.eShopWeb.Infrastructure.Identity; + +public class AppIdentityDbContextSeed { - public class AppIdentityDbContextSeed + public static async Task SeedAsync(UserManager userManager, RoleManager roleManager) { - public static async Task SeedAsync(UserManager userManager, RoleManager roleManager) - { - await roleManager.CreateAsync(new IdentityRole(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)); + await roleManager.CreateAsync(new IdentityRole(BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)); - var defaultUser = new ApplicationUser { UserName = "demouser@microsoft.com", Email = "demouser@microsoft.com" }; - await userManager.CreateAsync(defaultUser, AuthorizationConstants.DEFAULT_PASSWORD); + var defaultUser = new ApplicationUser { UserName = "demouser@microsoft.com", Email = "demouser@microsoft.com" }; + await userManager.CreateAsync(defaultUser, AuthorizationConstants.DEFAULT_PASSWORD); - string adminUserName = "admin@microsoft.com"; - var adminUser = new ApplicationUser { UserName = adminUserName, Email = adminUserName }; - await userManager.CreateAsync(adminUser, AuthorizationConstants.DEFAULT_PASSWORD); - adminUser = await userManager.FindByNameAsync(adminUserName); - await userManager.AddToRoleAsync(adminUser, BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); - } + string adminUserName = "admin@microsoft.com"; + var adminUser = new ApplicationUser { UserName = adminUserName, Email = adminUserName }; + await userManager.CreateAsync(adminUser, AuthorizationConstants.DEFAULT_PASSWORD); + adminUser = await userManager.FindByNameAsync(adminUserName); + await userManager.AddToRoleAsync(adminUser, BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS); } } diff --git a/src/Infrastructure/Identity/ApplicationUser.cs b/src/Infrastructure/Identity/ApplicationUser.cs index 9b87155bd..4a5105b64 100644 --- a/src/Infrastructure/Identity/ApplicationUser.cs +++ b/src/Infrastructure/Identity/ApplicationUser.cs @@ -1,8 +1,7 @@ using Microsoft.AspNetCore.Identity; -namespace Microsoft.eShopWeb.Infrastructure.Identity +namespace Microsoft.eShopWeb.Infrastructure.Identity; + +public class ApplicationUser : IdentityUser { - public class ApplicationUser : IdentityUser - { - } } diff --git a/src/Infrastructure/Identity/IdentityTokenClaimService.cs b/src/Infrastructure/Identity/IdentityTokenClaimService.cs index f2518031f..c45f3550e 100644 --- a/src/Infrastructure/Identity/IdentityTokenClaimService.cs +++ b/src/Infrastructure/Identity/IdentityTokenClaimService.cs @@ -1,46 +1,45 @@ -using Microsoft.AspNetCore.Identity; -using Microsoft.eShopWeb.ApplicationCore.Constants; -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using Microsoft.IdentityModel.Tokens; -using System; +using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.eShopWeb.ApplicationCore.Constants; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.eShopWeb.Infrastructure.Identity; -namespace Microsoft.eShopWeb.Infrastructure.Identity +public class IdentityTokenClaimService : ITokenClaimsService { - public class IdentityTokenClaimService : ITokenClaimsService + private readonly UserManager _userManager; + + public IdentityTokenClaimService(UserManager userManager) + { + _userManager = userManager; + } + + public async Task GetTokenAsync(string userName) { - private readonly UserManager _userManager; + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); + var user = await _userManager.FindByNameAsync(userName); + var roles = await _userManager.GetRolesAsync(user); + var claims = new List { new Claim(ClaimTypes.Name, userName) }; - public IdentityTokenClaimService(UserManager userManager) + foreach (var role in roles) { - _userManager = userManager; + claims.Add(new Claim(ClaimTypes.Role, role)); } - public async Task GetTokenAsync(string userName) + var tokenDescriptor = new SecurityTokenDescriptor { - var tokenHandler = new JwtSecurityTokenHandler(); - var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); - var user = await _userManager.FindByNameAsync(userName); - var roles = await _userManager.GetRolesAsync(user); - var claims = new List { new Claim(ClaimTypes.Name, userName) }; - - foreach(var role in roles) - { - claims.Add(new Claim(ClaimTypes.Role, role)); - } - - var tokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(claims.ToArray()), - Expires = DateTime.UtcNow.AddDays(7), - SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) - }; - var token = tokenHandler.CreateToken(tokenDescriptor); - return tokenHandler.WriteToken(token); - } + Subject = new ClaimsIdentity(claims.ToArray()), + Expires = DateTime.UtcNow.AddDays(7), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); } } diff --git a/src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.cs b/src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.cs index 1126ed174..d98aeebc9 100644 --- a/src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.cs +++ b/src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.cs @@ -1,219 +1,218 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -namespace Microsoft.eShopWeb.Infrastructure.Identity.Migrations +namespace Microsoft.eShopWeb.Infrastructure.Identity.Migrations; + +public partial class InitialIdentityModel : Migration { - public partial class InitialIdentityModel : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AspNetRoles", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetUsers", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetRoleClaims", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(type: "nvarchar(450)", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserClaims", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(type: "nvarchar(450)", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetUserClaims_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserLogins", - columns: table => new - { - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), - ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), - UserId = table.Column(type: "nvarchar(450)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_AspNetUserLogins_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserRoles", - columns: table => new - { - UserId = table.Column(type: "nvarchar(450)", nullable: false), - RoleId = table.Column(type: "nvarchar(450)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserTokens", - columns: table => new - { - UserId = table.Column(type: "nvarchar(450)", nullable: false), - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - Value = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AspNetUserTokens_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AspNetRoleClaims_RoleId", - table: "AspNetRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "AspNetRoles", - column: "NormalizedName", - unique: true, - filter: "[NormalizedName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserClaims_UserId", - table: "AspNetUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserLogins_UserId", - table: "AspNetUserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserRoles_RoleId", - table: "AspNetUserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "AspNetUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "AspNetUsers", - column: "NormalizedUserName", - unique: true, - filter: "[NormalizedUserName] IS NOT NULL"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AspNetRoleClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserLogins"); - - migrationBuilder.DropTable( - name: "AspNetUserRoles"); - - migrationBuilder.DropTable( - name: "AspNetUserTokens"); - - migrationBuilder.DropTable( - name: "AspNetRoles"); - - migrationBuilder.DropTable( - name: "AspNetUsers"); - } + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); } } diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index 0b916f912..2affa5f4a 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Microsoft.eShopWeb.Infrastructure diff --git a/src/Infrastructure/Logging/LoggerAdapter.cs b/src/Infrastructure/Logging/LoggerAdapter.cs index aea77e6ee..8cd9b457e 100644 --- a/src/Infrastructure/Logging/LoggerAdapter.cs +++ b/src/Infrastructure/Logging/LoggerAdapter.cs @@ -1,24 +1,23 @@ using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.Extensions.Logging; -namespace Microsoft.eShopWeb.Infrastructure.Logging +namespace Microsoft.eShopWeb.Infrastructure.Logging; + +public class LoggerAdapter : IAppLogger { - public class LoggerAdapter : IAppLogger + private readonly ILogger _logger; + public LoggerAdapter(ILoggerFactory loggerFactory) { - private readonly ILogger _logger; - public LoggerAdapter(ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(); - } + _logger = loggerFactory.CreateLogger(); + } - public void LogWarning(string message, params object[] args) - { - _logger.LogWarning(message, args); - } + public void LogWarning(string message, params object[] args) + { + _logger.LogWarning(message, args); + } - public void LogInformation(string message, params object[] args) - { - _logger.LogInformation(message, args); - } + public void LogInformation(string message, params object[] args) + { + _logger.LogInformation(message, args); } } diff --git a/src/Infrastructure/Services/EmailSender.cs b/src/Infrastructure/Services/EmailSender.cs index c6850c72e..2c70a6e19 100644 --- a/src/Infrastructure/Services/EmailSender.cs +++ b/src/Infrastructure/Services/EmailSender.cs @@ -1,16 +1,15 @@ -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.Infrastructure.Services +namespace Microsoft.eShopWeb.Infrastructure.Services; + +// This class is used by the application to send email for account confirmation and password reset. +// For more details see https://go.microsoft.com/fwlink/?LinkID=532713 +public class EmailSender : IEmailSender { - // This class is used by the application to send email for account confirmation and password reset. - // For more details see https://go.microsoft.com/fwlink/?LinkID=532713 - public class EmailSender : IEmailSender + public Task SendEmailAsync(string email, string subject, string message) { - public Task SendEmailAsync(string email, string subject, string message) - { - // TODO: Wire this up to actual email sending logic via SendGrid, local SMTP, etc. - return Task.CompletedTask; - } + // TODO: Wire this up to actual email sending logic via SendGrid, local SMTP, etc. + return Task.CompletedTask; } } diff --git a/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateRequest.cs b/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateRequest.cs index c6cd78da7..4059fdc71 100644 --- a/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateRequest.cs +++ b/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateRequest.cs @@ -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; } } diff --git a/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateResponse.cs b/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateResponse.cs index e98fd34c9..4c4b18d07 100644 --- a/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateResponse.cs +++ b/src/PublicApi/AuthEndpoints/Authenticate.AuthenticateResponse.cs @@ -1,21 +1,20 @@ using System; -namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints +namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints; + +public class AuthenticateResponse : BaseResponse { - public class AuthenticateResponse : BaseResponse + public AuthenticateResponse(Guid correlationId) : base(correlationId) { - 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; + 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; } diff --git a/src/PublicApi/AuthEndpoints/Authenticate.ClaimValue.cs b/src/PublicApi/AuthEndpoints/Authenticate.ClaimValue.cs index 6e57e5105..5296e7159 100644 --- a/src/PublicApi/AuthEndpoints/Authenticate.ClaimValue.cs +++ b/src/PublicApi/AuthEndpoints/Authenticate.ClaimValue.cs @@ -3,21 +3,20 @@ 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; } } diff --git a/src/PublicApi/AuthEndpoints/Authenticate.UserInfo.cs b/src/PublicApi/AuthEndpoints/Authenticate.UserInfo.cs index 49838abe2..8c55fbb72 100644 --- a/src/PublicApi/AuthEndpoints/Authenticate.UserInfo.cs +++ b/src/PublicApi/AuthEndpoints/Authenticate.UserInfo.cs @@ -3,14 +3,13 @@ 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 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 Claims { get; set; } } diff --git a/src/PublicApi/AuthEndpoints/Authenticate.cs b/src/PublicApi/AuthEndpoints/Authenticate.cs index 6a79af6bc..61d576dd7 100644 --- a/src/PublicApi/AuthEndpoints/Authenticate.cs +++ b/src/PublicApi/AuthEndpoints/Authenticate.cs @@ -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 -{ - public class Authenticate : BaseAsyncEndpoint - .WithRequest - .WithResponse - { - private readonly SignInManager _signInManager; - private readonly ITokenClaimsService _tokenClaimsService; +namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints; - public Authenticate(SignInManager signInManager, - ITokenClaimsService tokenClaimsService) - { - _signInManager = signInManager; - _tokenClaimsService = tokenClaimsService; - } +public class Authenticate : BaseAsyncEndpoint + .WithRequest + .WithResponse +{ + private readonly SignInManager _signInManager; + private readonly ITokenClaimsService _tokenClaimsService; - [HttpPost("api/authenticate")] - [SwaggerOperation( - Summary = "Authenticates a user", - Description = "Authenticates a user", - OperationId = "auth.authenticate", - Tags = new[] { "AuthEndpoints" }) - ] - public override async Task> HandleAsync(AuthenticateRequest request, CancellationToken cancellationToken) - { - var response = new AuthenticateResponse(request.CorrelationId()); + public Authenticate(SignInManager signInManager, + ITokenClaimsService tokenClaimsService) + { + _signInManager = signInManager; + _tokenClaimsService = tokenClaimsService; + } - // 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); + [HttpPost("api/authenticate")] + [SwaggerOperation( + Summary = "Authenticates a user", + Description = "Authenticates a user", + OperationId = "auth.authenticate", + Tags = new[] { "AuthEndpoints" }) + ] + public override async Task> HandleAsync(AuthenticateRequest request, CancellationToken cancellationToken) + { + var response = new AuthenticateResponse(request.CorrelationId()); - response.Result = result.Succeeded; - response.IsLockedOut = result.IsLockedOut; - response.IsNotAllowed = result.IsNotAllowed; - response.RequiresTwoFactor = result.RequiresTwoFactor; - response.Username = request.Username; + // 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); - if (result.Succeeded) - { - response.Token = await _tokenClaimsService.GetTokenAsync(request.Username); - } + response.Result = result.Succeeded; + response.IsLockedOut = result.IsLockedOut; + response.IsNotAllowed = result.IsNotAllowed; + response.RequiresTwoFactor = result.RequiresTwoFactor; + response.Username = request.Username; - return response; + if (result.Succeeded) + { + response.Token = await _tokenClaimsService.GetTokenAsync(request.Username); } + + return response; } } diff --git a/src/PublicApi/BaseMessage.cs b/src/PublicApi/BaseMessage.cs index 5ffcd21ed..28fb90689 100644 --- a/src/PublicApi/BaseMessage.cs +++ b/src/PublicApi/BaseMessage.cs @@ -1,16 +1,15 @@ using System; -namespace Microsoft.eShopWeb.PublicApi +namespace Microsoft.eShopWeb.PublicApi; + +/// +/// Base class used by API requests +/// +public abstract class BaseMessage { /// - /// Base class used by API requests + /// Unique Identifier used by logging /// - public abstract class BaseMessage - { - /// - /// Unique Identifier used by logging - /// - protected Guid _correlationId = Guid.NewGuid(); - public Guid CorrelationId() => _correlationId; - } + protected Guid _correlationId = Guid.NewGuid(); + public Guid CorrelationId() => _correlationId; } diff --git a/src/PublicApi/BaseRequest.cs b/src/PublicApi/BaseRequest.cs index f7c75fb3e..cc1cf01db 100644 --- a/src/PublicApi/BaseRequest.cs +++ b/src/PublicApi/BaseRequest.cs @@ -1,9 +1,8 @@ -namespace Microsoft.eShopWeb.PublicApi +namespace Microsoft.eShopWeb.PublicApi; + +/// +/// Base class used by API requests +/// +public abstract class BaseRequest : BaseMessage { - /// - /// Base class used by API requests - /// - public abstract class BaseRequest : BaseMessage - { - } } diff --git a/src/PublicApi/BaseResponse.cs b/src/PublicApi/BaseResponse.cs index 4ce849acb..f25669948 100644 --- a/src/PublicApi/BaseResponse.cs +++ b/src/PublicApi/BaseResponse.cs @@ -1,19 +1,18 @@ using System; -namespace Microsoft.eShopWeb.PublicApi +namespace Microsoft.eShopWeb.PublicApi; + +/// +/// Base class used by API responses +/// +public abstract class BaseResponse : BaseMessage { - /// - /// Base class used by API responses - /// - public abstract class BaseResponse : BaseMessage + public BaseResponse(Guid correlationId) : base() { - public BaseResponse(Guid correlationId) : base() - { - base._correlationId = correlationId; - } + base._correlationId = correlationId; + } - public BaseResponse() - { - } + public BaseResponse() + { } } diff --git a/src/PublicApi/CatalogBrandEndpoints/CatalogBrandDto.cs b/src/PublicApi/CatalogBrandEndpoints/CatalogBrandDto.cs index 626f96c20..3a3ac1b06 100644 --- a/src/PublicApi/CatalogBrandEndpoints/CatalogBrandDto.cs +++ b/src/PublicApi/CatalogBrandEndpoints/CatalogBrandDto.cs @@ -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; } } diff --git a/src/PublicApi/CatalogBrandEndpoints/List.ListCatalogBrandsResponse.cs b/src/PublicApi/CatalogBrandEndpoints/List.ListCatalogBrandsResponse.cs index ff22598ad..24f42e265 100644 --- a/src/PublicApi/CatalogBrandEndpoints/List.ListCatalogBrandsResponse.cs +++ b/src/PublicApi/CatalogBrandEndpoints/List.ListCatalogBrandsResponse.cs @@ -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 CatalogBrands { get; set; } = new List(); + public ListCatalogBrandsResponse() + { } + + public List CatalogBrands { get; set; } = new List(); } diff --git a/src/PublicApi/CatalogBrandEndpoints/List.cs b/src/PublicApi/CatalogBrandEndpoints/List.cs index 6a2f0aa25..1524f4db2 100644 --- a/src/PublicApi/CatalogBrandEndpoints/List.cs +++ b/src/PublicApi/CatalogBrandEndpoints/List.cs @@ -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 { - public class List : BaseAsyncEndpoint - .WithoutRequest - .WithResponse - { - private readonly IRepository _catalogBrandRepository; - private readonly IMapper _mapper; + private readonly IRepository _catalogBrandRepository; + private readonly IMapper _mapper; - public List(IRepository catalogBrandRepository, - IMapper mapper) - { - _catalogBrandRepository = catalogBrandRepository; - _mapper = mapper; - } + public List(IRepository catalogBrandRepository, + IMapper mapper) + { + _catalogBrandRepository = catalogBrandRepository; + _mapper = mapper; + } - [HttpGet("api/catalog-brands")] - [SwaggerOperation( - Summary = "List Catalog Brands", - Description = "List Catalog Brands", - OperationId = "catalog-brands.List", - Tags = new[] { "CatalogBrandEndpoints" }) - ] - public override async Task> HandleAsync(CancellationToken cancellationToken) - { - var response = new ListCatalogBrandsResponse(); + [HttpGet("api/catalog-brands")] + [SwaggerOperation( + Summary = "List Catalog Brands", + Description = "List Catalog Brands", + OperationId = "catalog-brands.List", + Tags = new[] { "CatalogBrandEndpoints" }) + ] + public override async Task> HandleAsync(CancellationToken cancellationToken) + { + var response = new ListCatalogBrandsResponse(); - var items = await _catalogBrandRepository.ListAsync(cancellationToken); + var items = await _catalogBrandRepository.ListAsync(cancellationToken); - response.CatalogBrands.AddRange(items.Select(_mapper.Map)); + response.CatalogBrands.AddRange(items.Select(_mapper.Map)); - return Ok(response); - } + return Ok(response); } } diff --git a/src/PublicApi/CatalogItemEndpoints/CatalogItemDto.cs b/src/PublicApi/CatalogItemEndpoints/CatalogItemDto.cs index ed8e302d4..edee27a81 100644 --- a/src/PublicApi/CatalogItemEndpoints/CatalogItemDto.cs +++ b/src/PublicApi/CatalogItemEndpoints/CatalogItemDto.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs index ef610768e..7ff63f8c2 100644 --- a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs +++ b/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemRequest.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemResponse.cs index e0c2f4550..6edfda613 100644 --- a/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemResponse.cs +++ b/src/PublicApi/CatalogItemEndpoints/Create.CreateCatalogItemResponse.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/Create.cs b/src/PublicApi/CatalogItemEndpoints/Create.cs index 409f50c36..74ee7ed1a 100644 --- a/src/PublicApi/CatalogItemEndpoints/Create.cs +++ b/src/PublicApi/CatalogItemEndpoints/Create.cs @@ -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.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 + .WithResponse { + private readonly IRepository _itemRepository; + private readonly IUriComposer _uriComposer; - [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class Create : BaseAsyncEndpoint - .WithRequest - .WithResponse + public Create(IRepository itemRepository, + IUriComposer uriComposer) { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; + _itemRepository = itemRepository; + _uriComposer = uriComposer; + } - public Create(IRepository 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> HandleAsync(CreateCatalogItemRequest request, CancellationToken cancellationToken) + { + var response = new CreateCatalogItemResponse(request.CorrelationId()); - [HttpPost("api/catalog-items")] - [SwaggerOperation( - Summary = "Creates a new Catalog Item", - Description = "Creates a new Catalog Item", - OperationId = "catalog-items.create", - Tags = new[] { "CatalogItemEndpoints" }) - ] - public override async Task> HandleAsync(CreateCatalogItemRequest request, CancellationToken cancellationToken) + var catalogItemNameSpecification = new CatalogItemNameSpecification(request.Name); + var existingCataloogItem = await _itemRepository.CountAsync(catalogItemNameSpecification, cancellationToken); + if (existingCataloogItem > 0) { - 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); + throw new DuplicateException($"A catalogItem with name {request.Name} already exists"); + } - 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. + var newItem = new CatalogItem(request.CatalogTypeId, request.CatalogBrandId, request.Description, request.Name, request.Price, request.PictureUri); + newItem = await _itemRepository.AddAsync(newItem, cancellationToken); - newItem.UpdatePictureUri("eCatalog-item-default.png"); - await _itemRepository.UpdateAsync(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. - 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; + 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; } + + } diff --git a/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemRequest.cs index 29aecd9a5..4fee76f83 100644 --- a/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemRequest.cs +++ b/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemRequest.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemResponse.cs index 33954b2a0..f05847f40 100644 --- a/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemResponse.cs +++ b/src/PublicApi/CatalogItemEndpoints/Delete.DeleteCatalogItemResponse.cs @@ -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"; } diff --git a/src/PublicApi/CatalogItemEndpoints/Delete.cs b/src/PublicApi/CatalogItemEndpoints/Delete.cs index 960c0644e..9b1173204 100644 --- a/src/PublicApi/CatalogItemEndpoints/Delete.cs +++ b/src/PublicApi/CatalogItemEndpoints/Delete.cs @@ -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 + .WithResponse { - [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class Delete : BaseAsyncEndpoint - .WithRequest - .WithResponse - { - private readonly IRepository _itemRepository; + private readonly IRepository _itemRepository; - public Delete(IRepository itemRepository) - { - _itemRepository = itemRepository; - } + public Delete(IRepository itemRepository) + { + _itemRepository = itemRepository; + } - [HttpDelete("api/catalog-items/{CatalogItemId}")] - [SwaggerOperation( - Summary = "Deletes a Catalog Item", - Description = "Deletes a Catalog Item", - OperationId = "catalog-items.Delete", - Tags = new[] { "CatalogItemEndpoints" }) - ] - public override async Task> HandleAsync([FromRoute] DeleteCatalogItemRequest request, CancellationToken cancellationToken) - { - var response = new DeleteCatalogItemResponse(request.CorrelationId()); + [HttpDelete("api/catalog-items/{CatalogItemId}")] + [SwaggerOperation( + Summary = "Deletes a Catalog Item", + Description = "Deletes a Catalog Item", + OperationId = "catalog-items.Delete", + Tags = new[] { "CatalogItemEndpoints" }) + ] + public override async Task> HandleAsync([FromRoute] DeleteCatalogItemRequest request, CancellationToken cancellationToken) + { + var response = new DeleteCatalogItemResponse(request.CorrelationId()); - var itemToDelete = await _itemRepository.GetByIdAsync(request.CatalogItemId, cancellationToken); - if (itemToDelete is null) return NotFound(); + 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); } } diff --git a/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemRequest.cs index 27f85f9aa..ab0b2fb92 100644 --- a/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemRequest.cs +++ b/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemRequest.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemResponse.cs index ebc7c7807..0745e804b 100644 --- a/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemResponse.cs +++ b/src/PublicApi/CatalogItemEndpoints/GetById.GetByIdCatalogItemResponse.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/GetById.cs b/src/PublicApi/CatalogItemEndpoints/GetById.cs index 29501bbd4..722f221f6 100644 --- a/src/PublicApi/CatalogItemEndpoints/GetById.cs +++ b/src/PublicApi/CatalogItemEndpoints/GetById.cs @@ -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 + .WithResponse { - public class GetById : BaseAsyncEndpoint - .WithRequest - .WithResponse - { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; + private readonly IRepository _itemRepository; + private readonly IUriComposer _uriComposer; - public GetById(IRepository itemRepository, IUriComposer uriComposer) - { - _itemRepository = itemRepository; - _uriComposer = uriComposer; - } + public GetById(IRepository itemRepository, IUriComposer uriComposer) + { + _itemRepository = itemRepository; + _uriComposer = uriComposer; + } - [HttpGet("api/catalog-items/{CatalogItemId}")] - [SwaggerOperation( - Summary = "Get a Catalog Item by Id", - Description = "Gets a Catalog Item by Id", - OperationId = "catalog-items.GetById", - Tags = new[] { "CatalogItemEndpoints" }) - ] - public override async Task> HandleAsync([FromRoute] GetByIdCatalogItemRequest request, CancellationToken cancellationToken) - { - var response = new GetByIdCatalogItemResponse(request.CorrelationId()); + [HttpGet("api/catalog-items/{CatalogItemId}")] + [SwaggerOperation( + Summary = "Get a Catalog Item by Id", + Description = "Gets a Catalog Item by Id", + OperationId = "catalog-items.GetById", + Tags = new[] { "CatalogItemEndpoints" }) + ] + public override async Task> HandleAsync([FromRoute] GetByIdCatalogItemRequest request, CancellationToken cancellationToken) + { + var response = new GetByIdCatalogItemResponse(request.CorrelationId()); - var item = await _itemRepository.GetByIdAsync(request.CatalogItemId, cancellationToken); - if (item is null) return NotFound(); + 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); - } + response.CatalogItem = new CatalogItemDto + { + Id = item.Id, + CatalogBrandId = item.CatalogBrandId, + CatalogTypeId = item.CatalogTypeId, + Description = item.Description, + Name = item.Name, + PictureUri = _uriComposer.ComposePicUri(item.PictureUri), + Price = item.Price + }; + return Ok(response); } } diff --git a/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemRequest.cs index a0451b984..7c48e9b61 100644 --- a/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemRequest.cs +++ b/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemRequest.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemResponse.cs index 9d95ee64f..84799dd97 100644 --- a/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemResponse.cs +++ b/src/PublicApi/CatalogItemEndpoints/ListPaged.ListPagedCatalogItemResponse.cs @@ -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 CatalogItems { get; set; } = new List(); - public int PageCount { get; set; } + public ListPagedCatalogItemResponse() + { } + + public List CatalogItems { get; set; } = new List(); + public int PageCount { get; set; } } diff --git a/src/PublicApi/CatalogItemEndpoints/ListPaged.cs b/src/PublicApi/CatalogItemEndpoints/ListPaged.cs index d6f0d081e..9feffa7d8 100644 --- a/src/PublicApi/CatalogItemEndpoints/ListPaged.cs +++ b/src/PublicApi/CatalogItemEndpoints/ListPaged.cs @@ -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 -{ - public class ListPaged : BaseAsyncEndpoint - .WithRequest - .WithResponse - { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; - private readonly IMapper _mapper; +namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; - public ListPaged(IRepository itemRepository, - IUriComposer uriComposer, - IMapper mapper) - { - _itemRepository = itemRepository; - _uriComposer = uriComposer; - _mapper = mapper; - } +public class ListPaged : BaseAsyncEndpoint + .WithRequest + .WithResponse +{ + private readonly IRepository _itemRepository; + private readonly IUriComposer _uriComposer; + private readonly 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> HandleAsync([FromQuery] ListPagedCatalogItemRequest request, CancellationToken cancellationToken) - { - var response = new ListPagedCatalogItemResponse(request.CorrelationId()); + public ListPaged(IRepository itemRepository, + IUriComposer uriComposer, + IMapper mapper) + { + _itemRepository = itemRepository; + _uriComposer = uriComposer; + _mapper = mapper; + } - var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId); - int totalItems = await _itemRepository.CountAsync(filterSpec, cancellationToken); + [HttpGet("api/catalog-items")] + [SwaggerOperation( + Summary = "List Catalog Items (paged)", + Description = "List Catalog Items (paged)", + OperationId = "catalog-items.ListPaged", + Tags = new[] { "CatalogItemEndpoints" }) + ] + public override async Task> HandleAsync([FromQuery] ListPagedCatalogItemRequest request, CancellationToken cancellationToken) + { + var response = new ListPagedCatalogItemResponse(request.CorrelationId()); - var pagedSpec = new CatalogFilterPaginatedSpecification( - skip: request.PageIndex * request.PageSize, - take: request.PageSize, - brandId: request.CatalogBrandId, - typeId: request.CatalogTypeId); + var filterSpec = new CatalogFilterSpecification(request.CatalogBrandId, request.CatalogTypeId); + int totalItems = await _itemRepository.CountAsync(filterSpec, cancellationToken); - var items = await _itemRepository.ListAsync(pagedSpec, cancellationToken); + var pagedSpec = new CatalogFilterPaginatedSpecification( + skip: request.PageIndex * request.PageSize, + take: request.PageSize, + brandId: request.CatalogBrandId, + typeId: request.CatalogTypeId); - response.CatalogItems.AddRange(items.Select(_mapper.Map)); - foreach (CatalogItemDto item in response.CatalogItems) - { - item.PictureUri = _uriComposer.ComposePicUri(item.PictureUri); - } + var items = await _itemRepository.ListAsync(pagedSpec, cancellationToken); - if (request.PageSize > 0) - { - response.PageCount = int.Parse(Math.Ceiling((decimal)totalItems / request.PageSize).ToString()); - } - else - { - response.PageCount = totalItems > 0 ? 1 : 0; - } + response.CatalogItems.AddRange(items.Select(_mapper.Map)); + foreach (CatalogItemDto item in response.CatalogItems) + { + item.PictureUri = _uriComposer.ComposePicUri(item.PictureUri); + } - return Ok(response); + 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); } } diff --git a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs index d386fded4..04f8d559d 100644 --- a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs +++ b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemRequest.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs index 12913b5a0..7f7e8b9ad 100644 --- a/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs +++ b/src/PublicApi/CatalogItemEndpoints/Update.UpdateCatalogItemResponse.cs @@ -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; } } diff --git a/src/PublicApi/CatalogItemEndpoints/Update.cs b/src/PublicApi/CatalogItemEndpoints/Update.cs index bdd4cf868..3d674b216 100644 --- a/src/PublicApi/CatalogItemEndpoints/Update.cs +++ b/src/PublicApi/CatalogItemEndpoints/Update.cs @@ -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 + .WithResponse { - [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class Update : BaseAsyncEndpoint - .WithRequest - .WithResponse + private readonly IRepository _itemRepository; + private readonly IUriComposer _uriComposer; + + public Update(IRepository itemRepository, IUriComposer uriComposer) { - private readonly IRepository _itemRepository; - private readonly IUriComposer _uriComposer; + _itemRepository = itemRepository; + _uriComposer = uriComposer; + } - public Update(IRepository itemRepository, IUriComposer uriComposer) - { - _itemRepository = itemRepository; - _uriComposer = uriComposer; - } - - [HttpPut("api/catalog-items")] - [SwaggerOperation( - Summary = "Updates a Catalog Item", - Description = "Updates a Catalog Item", - OperationId = "catalog-items.update", - Tags = new[] { "CatalogItemEndpoints" }) - ] - public override async Task> HandleAsync(UpdateCatalogItemRequest request, CancellationToken cancellationToken) + [HttpPut("api/catalog-items")] + [SwaggerOperation( + Summary = "Updates a Catalog Item", + Description = "Updates a Catalog Item", + OperationId = "catalog-items.update", + Tags = new[] { "CatalogItemEndpoints" }) + ] + public override async Task> HandleAsync(UpdateCatalogItemRequest request, CancellationToken cancellationToken) + { + var response = new UpdateCatalogItemResponse(request.CorrelationId()); + + var existingItem = await _itemRepository.GetByIdAsync(request.Id, cancellationToken); + + existingItem.UpdateDetails(request.Name, request.Description, request.Price); + existingItem.UpdateBrand(request.CatalogBrandId); + existingItem.UpdateType(request.CatalogTypeId); + + await _itemRepository.UpdateAsync(existingItem, cancellationToken); + + var dto = new CatalogItemDto { - 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; } } diff --git a/src/PublicApi/CatalogTypeEndpoints/CatalogTypeDto.cs b/src/PublicApi/CatalogTypeEndpoints/CatalogTypeDto.cs index 6e21006fc..d9dae3f38 100644 --- a/src/PublicApi/CatalogTypeEndpoints/CatalogTypeDto.cs +++ b/src/PublicApi/CatalogTypeEndpoints/CatalogTypeDto.cs @@ -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; } } diff --git a/src/PublicApi/CatalogTypeEndpoints/List.ListCatalogTypesResponse.cs b/src/PublicApi/CatalogTypeEndpoints/List.ListCatalogTypesResponse.cs index 5b4b4765c..34ae58ef9 100644 --- a/src/PublicApi/CatalogTypeEndpoints/List.ListCatalogTypesResponse.cs +++ b/src/PublicApi/CatalogTypeEndpoints/List.ListCatalogTypesResponse.cs @@ -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 CatalogTypes { get; set; } = new List(); + public ListCatalogTypesResponse() + { } + + public List CatalogTypes { get; set; } = new List(); } diff --git a/src/PublicApi/CatalogTypeEndpoints/List.cs b/src/PublicApi/CatalogTypeEndpoints/List.cs index f25c4a086..7e40aac88 100644 --- a/src/PublicApi/CatalogTypeEndpoints/List.cs +++ b/src/PublicApi/CatalogTypeEndpoints/List.cs @@ -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 { - public class List : BaseAsyncEndpoint - .WithoutRequest - .WithResponse - { - private readonly IRepository _catalogTypeRepository; - private readonly IMapper _mapper; + private readonly IRepository _catalogTypeRepository; + private readonly IMapper _mapper; - public List(IRepository catalogTypeRepository, - IMapper mapper) - { - _catalogTypeRepository = catalogTypeRepository; - _mapper = mapper; - } + public List(IRepository catalogTypeRepository, + IMapper mapper) + { + _catalogTypeRepository = catalogTypeRepository; + _mapper = mapper; + } - [HttpGet("api/catalog-types")] - [SwaggerOperation( - Summary = "List Catalog Types", - Description = "List Catalog Types", - OperationId = "catalog-types.List", - Tags = new[] { "CatalogTypeEndpoints" }) - ] - public override async Task> HandleAsync(CancellationToken cancellationToken) - { - var response = new ListCatalogTypesResponse(); + [HttpGet("api/catalog-types")] + [SwaggerOperation( + Summary = "List Catalog Types", + Description = "List Catalog Types", + OperationId = "catalog-types.List", + Tags = new[] { "CatalogTypeEndpoints" }) + ] + public override async Task> HandleAsync(CancellationToken cancellationToken) + { + var response = new ListCatalogTypesResponse(); - var items = await _catalogTypeRepository.ListAsync(cancellationToken); + var items = await _catalogTypeRepository.ListAsync(cancellationToken); - response.CatalogTypes.AddRange(items.Select(_mapper.Map)); + response.CatalogTypes.AddRange(items.Select(_mapper.Map)); - return Ok(response); - } + return Ok(response); } } diff --git a/src/PublicApi/CustomSchemaFilters.cs b/src/PublicApi/CustomSchemaFilters.cs index 854e8e3cb..d5f538b79 100644 --- a/src/PublicApi/CustomSchemaFilters.cs +++ b/src/PublicApi/CustomSchemaFilters.cs @@ -1,17 +1,16 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Microsoft.eShopWeb.PublicApi +namespace Microsoft.eShopWeb.PublicApi; + +public class CustomSchemaFilters : ISchemaFilter { - public class CustomSchemaFilters : ISchemaFilter + public void Apply(OpenApiSchema schema, SchemaFilterContext context) { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) - { - var excludeProperties = new[] { "CorrelationId" }; + var excludeProperties = new[] { "CorrelationId" }; - foreach (var prop in excludeProperties) - if (schema.Properties.ContainsKey(prop)) - schema.Properties.Remove(prop); - } + foreach (var prop in excludeProperties) + if (schema.Properties.ContainsKey(prop)) + schema.Properties.Remove(prop); } } diff --git a/src/PublicApi/ImageValidators.cs b/src/PublicApi/ImageValidators.cs index 4756ca00b..6879e466d 100644 --- a/src/PublicApi/ImageValidators.cs +++ b/src/PublicApi/ImageValidators.cs @@ -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; + private const int ImageMaximumBytes = 512000; - public static bool IsValidImage(this byte[] postedFile, string fileName) - { - 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); } } diff --git a/src/PublicApi/MappingProfile.cs b/src/PublicApi/MappingProfile.cs index acbac99e3..c6b57ab98 100644 --- a/src/PublicApi/MappingProfile.cs +++ b/src/PublicApi/MappingProfile.cs @@ -4,17 +4,16 @@ 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(); - CreateMap() - .ForMember(dto => dto.Name, options => options.MapFrom(src => src.Type)); - CreateMap() - .ForMember(dto => dto.Name, options => options.MapFrom(src => src.Brand)); - } + CreateMap(); + CreateMap() + .ForMember(dto => dto.Name, options => options.MapFrom(src => src.Type)); + CreateMap() + .ForMember(dto => dto.Name, options => options.MapFrom(src => src.Brand)); } } diff --git a/src/PublicApi/MiddleWares/ExceptionMiddleware.cs b/src/PublicApi/MiddleWares/ExceptionMiddleware.cs index 9115f7427..bae4800a0 100644 --- a/src/PublicApi/MiddleWares/ExceptionMiddleware.cs +++ b/src/PublicApi/MiddleWares/ExceptionMiddleware.cs @@ -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) - { - context.Response.ContentType = "application/json"; + private async Task HandleExceptionAsync(HttpContext context, Exception exception) + { + context.Response.ContentType = "application/json"; - if (exception is DuplicateException duplicationException) + 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()); } } } diff --git a/src/PublicApi/Program.cs b/src/PublicApi/Program.cs index 7be780502..a1c2b2448 100644 --- a/src/PublicApi/Program.cs +++ b/src/PublicApi/Program.cs @@ -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.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(); + var host = CreateHostBuilder(args) + .Build(); - using (var scope = host.Services.CreateScope()) + using (var scope = host.Services.CreateScope()) + { + var services = scope.ServiceProvider; + var loggerFactory = services.GetRequiredService(); + try { - var services = scope.ServiceProvider; - var loggerFactory = services.GetRequiredService(); - try - { - var catalogContext = services.GetRequiredService(); - await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory); + var catalogContext = services.GetRequiredService(); + await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory); - var userManager = services.GetRequiredService>(); - var roleManager = services.GetRequiredService>(); - await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); - } - catch (Exception ex) - { - var logger = loggerFactory.CreateLogger(); - logger.LogError(ex, "An error occurred seeding the DB."); - } + var userManager = services.GetRequiredService>(); + var roleManager = services.GetRequiredService>(); + await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); + } + catch (Exception ex) + { + var logger = loggerFactory.CreateLogger(); + logger.LogError(ex, "An error occurred seeding the DB."); } - - host.Run(); } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + host.Run(); } + + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } diff --git a/src/PublicApi/PublicApi.csproj b/src/PublicApi/PublicApi.csproj index 7d99e4195..e3fdee65e 100644 --- a/src/PublicApi/PublicApi.csproj +++ b/src/PublicApi/PublicApi.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Microsoft.eShopWeb.PublicApi 5b662463-1efd-4bae-bde4-befe0be3e8ff Linux diff --git a/src/PublicApi/Startup.cs b/src/PublicApi/Startup.cs index 401ac5617..9296c9564 100644 --- a/src/PublicApi/Startup.cs +++ b/src/PublicApi/Startup.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using System.Text; using AutoMapper; using BlazorShared; using MediatR; @@ -20,138 +22,136 @@ 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"; + private const string CORS_POLICY = "CorsPolicy"; - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - public void ConfigureDevelopmentServices(IServiceCollection services) - { - // use in-memory database - ConfigureInMemoryDatabases(services); + public void ConfigureDevelopmentServices(IServiceCollection services) + { + // use in-memory database + ConfigureInMemoryDatabases(services); - // use real database - //ConfigureProductionServices(services); - } + // use real database + //ConfigureProductionServices(services); + } - public void ConfigureDockerServices(IServiceCollection services) - { - ConfigureDevelopmentServices(services); - } + public void ConfigureDockerServices(IServiceCollection services) + { + ConfigureDevelopmentServices(services); + } - private void ConfigureInMemoryDatabases(IServiceCollection services) - { - services.AddDbContext(c => - c.UseInMemoryDatabase("Catalog")); + private void ConfigureInMemoryDatabases(IServiceCollection services) + { + services.AddDbContext(c => + c.UseInMemoryDatabase("Catalog")); - services.AddDbContext(options => - options.UseInMemoryDatabase("Identity")); + services.AddDbContext(options => + options.UseInMemoryDatabase("Identity")); - ConfigureServices(services); - } + 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(c => - c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection"))); + 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(c => + c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection"))); - // Add Identity DbContext - services.AddDbContext(options => - options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection"))); + // Add Identity DbContext + services.AddDbContext(options => + options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection"))); - ConfigureServices(services); - } + ConfigureServices(services); + } - public void ConfigureTestingServices(IServiceCollection services) - { - ConfigureInMemoryDatabases(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() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); - services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); - services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>)); - services.Configure(Configuration); - services.AddSingleton(new UriComposer(Configuration.Get())); - services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>)); - services.AddScoped(); + services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); + services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>)); + services.Configure(Configuration); + services.AddSingleton(new UriComposer(Configuration.Get())); + services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>)); + services.AddScoped(); - var baseUrlConfig = new BaseUrlConfiguration(); - Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); + var baseUrlConfig = new BaseUrlConfiguration(); + Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); - services.AddMemoryCache(); + services.AddMemoryCache(); - var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); - services.AddAuthentication(config => - { - config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(config => + var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); + services.AddAuthentication(config => + { + config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(config => + { + config.RequireHttpsMetadata = false; + config.SaveToken = true; + config.TokenValidationParameters = new TokenValidationParameters { - config.RequireHttpsMetadata = false; - config.SaveToken = true; - config.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey(key), - ValidateIssuer = false, - ValidateAudience = false - }; - }); - - services.AddCors(options => + 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.AddControllers(); + services.AddMediatR(typeof(CatalogItem).Assembly); + + services.AddAutoMapper(typeof(Startup).Assembly); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); + c.EnableAnnotations(); + c.SchemaFilter(); + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { - options.AddPolicy(name: CORS_POLICY, - builder => - { - builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/')); - builder.AllowAnyMethod(); - builder.AllowAnyHeader(); - }); + 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" }); - services.AddControllers(); - services.AddMediatR(typeof(CatalogItem).Assembly); - - services.AddAutoMapper(typeof(Startup).Assembly); - services.AddSwaggerGen(c => + c.AddSecurityRequirement(new OpenApiSecurityRequirement() { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); - c.EnableAnnotations(); - c.SchemaFilter(); - c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme - { - 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" - }); - - c.AddSecurityRequirement(new OpenApiSecurityRequirement() - { { new OpenApiSecurityScheme { @@ -167,42 +167,41 @@ public void ConfigureServices(IServiceCollection services) }, new List() } - }); }); - } + }); + } - // 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.UseDeveloperExceptionPage(); + } - app.UseMiddleware(); + app.UseMiddleware(); - app.UseHttpsRedirection(); + app.UseHttpsRedirection(); - app.UseRouting(); + app.UseRouting(); - app.UseCors(CORS_POLICY); + app.UseCors(CORS_POLICY); - app.UseAuthorization(); + app.UseAuthorization(); - // Enable middleware to serve generated Swagger as a JSON endpoint. - app.UseSwagger(); + // 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"); - }); + // 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.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); } } diff --git a/src/Web/Areas/Identity/IdentityHostingStartup.cs b/src/Web/Areas/Identity/IdentityHostingStartup.cs index ff8deb526..cf6c773fd 100644 --- a/src/Web/Areas/Identity/IdentityHostingStartup.cs +++ b/src/Web/Areas/Identity/IdentityHostingStartup.cs @@ -1,14 +1,14 @@ using Microsoft.AspNetCore.Hosting; [assembly: HostingStartup(typeof(Microsoft.eShopWeb.Web.Areas.Identity.IdentityHostingStartup))] -namespace Microsoft.eShopWeb.Web.Areas.Identity +namespace Microsoft.eShopWeb.Web.Areas.Identity; + +public class IdentityHostingStartup : IHostingStartup { - public class IdentityHostingStartup : IHostingStartup + public void Configure(IWebHostBuilder builder) { - public void Configure(IWebHostBuilder builder) + builder.ConfigureServices((context, services) => { - builder.ConfigureServices((context, services) => { - }); - } + }); } -} \ No newline at end of file +} diff --git a/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs index b1137686b..f85a02b3d 100644 --- a/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs +++ b/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs @@ -8,38 +8,37 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.eShopWeb.Infrastructure.Identity; -namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account +namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class ConfirmEmailModel : PageModel { - [AllowAnonymous] - public class ConfirmEmailModel : PageModel + private readonly UserManager _userManager; + + public ConfirmEmailModel(UserManager userManager) { - private readonly UserManager _userManager; + _userManager = userManager; + } - public ConfirmEmailModel(UserManager userManager) + public async Task OnGetAsync(string userId, string code) + { + if (userId == null || code == null) { - _userManager = userManager; + return RedirectToPage("/Index"); } - public async Task OnGetAsync(string userId, string code) + var user = await _userManager.FindByIdAsync(userId); + if (user == null) { - if (userId == null || code == null) - { - return RedirectToPage("/Index"); - } - - var user = await _userManager.FindByIdAsync(userId); - if (user == null) - { - return NotFound($"Unable to load user with ID '{userId}'."); - } - - var result = await _userManager.ConfirmEmailAsync(user, code); - if (!result.Succeeded) - { - throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':"); - } + return NotFound($"Unable to load user with ID '{userId}'."); + } - return Page(); + var result = await _userManager.ConfirmEmailAsync(user, code); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':"); } + + return Page(); } } diff --git a/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs index 1499e10f4..4d04c069e 100644 --- a/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs +++ b/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -1,4 +1,9 @@ -using Microsoft.AspNetCore.Authentication; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; @@ -7,117 +12,111 @@ using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account +namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class LoginModel : PageModel { - [AllowAnonymous] - public class LoginModel : PageModel + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + private readonly IBasketService _basketService; + + public LoginModel(SignInManager signInManager, ILogger logger, IBasketService basketService) { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - private readonly IBasketService _basketService; + _signInManager = signInManager; + _logger = logger; + _basketService = basketService; + } - public LoginModel(SignInManager signInManager, ILogger logger, IBasketService basketService) - { - _signInManager = signInManager; - _logger = logger; - _basketService = basketService; - } + [BindProperty] + public InputModel Input { get; set; } - [BindProperty] - public InputModel Input { get; set; } + public IList ExternalLogins { get; set; } - public IList ExternalLogins { get; set; } + public string ReturnUrl { get; set; } - public string ReturnUrl { get; set; } + [TempData] + public string ErrorMessage { get; set; } - [TempData] - public string ErrorMessage { get; set; } + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } - [Display(Name = "Remember me?")] - public bool RememberMe { get; set; } + public async Task OnGetAsync(string returnUrl = null) + { + if (!string.IsNullOrEmpty(ErrorMessage)) + { + ModelState.AddModelError(string.Empty, ErrorMessage); } - public async Task OnGetAsync(string returnUrl = null) - { - if (!string.IsNullOrEmpty(ErrorMessage)) - { - ModelState.AddModelError(string.Empty, ErrorMessage); - } + returnUrl = returnUrl ?? Url.Content("~/"); - returnUrl = returnUrl ?? Url.Content("~/"); + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + ReturnUrl = returnUrl; + } - ReturnUrl = returnUrl; - } + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); - public async Task OnPostAsync(string returnUrl = null) + if (ModelState.IsValid) { - returnUrl = returnUrl ?? Url.Content("~/"); + // 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(Input.Email, Input.Password, false, true); - if (ModelState.IsValid) + if (result.Succeeded) { - // 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(Input.Email, Input.Password, false, true); - - if (result.Succeeded) - { - _logger.LogInformation("User logged in."); - await TransferAnonymousBasketToUserAsync(Input.Email); - return LocalRedirect(returnUrl); - } - if (result.RequiresTwoFactor) - { - return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); - } - if (result.IsLockedOut) - { - _logger.LogWarning("User account locked out."); - return RedirectToPage("./Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return Page(); - } + _logger.LogInformation("User logged in."); + await TransferAnonymousBasketToUserAsync(Input.Email); + return LocalRedirect(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); + } + if (result.IsLockedOut) + { + _logger.LogWarning("User account locked out."); + return RedirectToPage("./Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return Page(); } - - // If we got this far, something failed, redisplay form - return Page(); } - private async Task TransferAnonymousBasketToUserAsync(string userName) + // If we got this far, something failed, redisplay form + return Page(); + } + + private async Task TransferAnonymousBasketToUserAsync(string userName) + { + if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) { - if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) + var anonymousId = Request.Cookies[Constants.BASKET_COOKIENAME]; + if (Guid.TryParse(anonymousId, out var _)) { - var anonymousId = Request.Cookies[Constants.BASKET_COOKIENAME]; - if (Guid.TryParse(anonymousId, out var _)) - { - await _basketService.TransferBasketAsync(anonymousId, userName); - } - Response.Cookies.Delete(Constants.BASKET_COOKIENAME); + await _basketService.TransferBasketAsync(anonymousId, userName); } + Response.Cookies.Delete(Constants.BASKET_COOKIENAME); } } } diff --git a/src/Web/Areas/Identity/Pages/Account/Logout.cshtml.cs b/src/Web/Areas/Identity/Pages/Account/Logout.cshtml.cs index 9b4b9e640..b6700e7ef 100644 --- a/src/Web/Areas/Identity/Pages/Account/Logout.cshtml.cs +++ b/src/Web/Areas/Identity/Pages/Account/Logout.cshtml.cs @@ -1,4 +1,8 @@ -using Microsoft.AspNetCore.Authentication; +using System; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -7,51 +11,46 @@ using Microsoft.eShopWeb.Web.Configuration; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account +namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account; + +//TODO : replace IMemoryCache by distributed cache if you are in multi-host scenario +public class LogoutModel : PageModel { - //TODO : replace IMemoryCache by distributed cache if you are in multi-host scenario - public class LogoutModel : PageModel + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + private readonly IMemoryCache _cache; + + public LogoutModel(SignInManager signInManager, ILogger logger, IMemoryCache cache) { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - private readonly IMemoryCache _cache; + _signInManager = signInManager; + _logger = logger; + _cache = cache; + } + + public void OnGet() + { + } - public LogoutModel(SignInManager signInManager, ILogger logger, IMemoryCache cache) + public async Task OnPost(string returnUrl = null) + { + await _signInManager.SignOutAsync(); + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + var userId = _signInManager.Context.User.Claims.First(c => c.Type == ClaimTypes.Name); + var identityKey = _signInManager.Context.Request.Cookies[ConfigureCookieSettings.IdentifierCookieName]; + _cache.Set($"{userId.Value}:{identityKey}", identityKey, new MemoryCacheEntryOptions { - _signInManager = signInManager; - _logger = logger; - _cache = cache; - } + AbsoluteExpiration = DateTime.Now.AddMinutes(ConfigureCookieSettings.ValidityMinutesPeriod) + }); - public void OnGet() + _logger.LogInformation("User logged out."); + if (returnUrl != null) { + return LocalRedirect(returnUrl); } - - public async Task OnPost(string returnUrl = null) + else { - await _signInManager.SignOutAsync(); - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - var userId = _signInManager.Context.User.Claims.First(c => c.Type == ClaimTypes.Name); - var identityKey = _signInManager.Context.Request.Cookies[ConfigureCookieSettings.IdentifierCookieName]; - _cache.Set($"{userId.Value}:{identityKey}", identityKey, new MemoryCacheEntryOptions - { - AbsoluteExpiration = DateTime.Now.AddMinutes(ConfigureCookieSettings.ValidityMinutesPeriod) - }); - - _logger.LogInformation("User logged out."); - if (returnUrl != null) - { - return LocalRedirect(returnUrl); - } - else - { - return RedirectToPage("/Index"); - } + return RedirectToPage("/Index"); } } -} \ No newline at end of file +} diff --git a/src/Web/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/Web/Areas/Identity/Pages/Account/Register.cshtml.cs index d2ea1792a..120c0bf18 100644 --- a/src/Web/Areas/Identity/Pages/Account/Register.cshtml.cs +++ b/src/Web/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -11,89 +11,88 @@ using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.Extensions.Logging; -namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account +namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class RegisterModel : PageModel { - [AllowAnonymous] - public class RegisterModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly UserManager _userManager; - private readonly ILogger _logger; - private readonly IEmailSender _emailSender; + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly ILogger _logger; + private readonly IEmailSender _emailSender; - public RegisterModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger, - IEmailSender emailSender) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - _emailSender = emailSender; - } + public RegisterModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger, + IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + _emailSender = emailSender; + } - [BindProperty] - public InputModel Input { get; set; } + [BindProperty] + public InputModel Input { get; set; } - public string ReturnUrl { get; set; } + public string ReturnUrl { get; set; } - public class InputModel - { - [Required] - [EmailAddress] - [Display(Name = "Email")] - public string Email { get; set; } + public class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "Password")] - public string Password { get; set; } + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } - public void OnGet(string returnUrl = null) - { - ReturnUrl = returnUrl; - } + public void OnGet(string returnUrl = null) + { + ReturnUrl = returnUrl; + } - public async Task OnPostAsync(string returnUrl = null) + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + if (ModelState.IsValid) { - returnUrl = returnUrl ?? Url.Content("~/"); - if (ModelState.IsValid) + var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; + var result = await _userManager.CreateAsync(user, Input.Password); + if (result.Succeeded) { - var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; - var result = await _userManager.CreateAsync(user, Input.Password); - if (result.Succeeded) - { - _logger.LogInformation("User created a new account with password."); + _logger.LogInformation("User created a new account with password."); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { userId = user.Id, code = code }, - protocol: Request.Scheme); + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { userId = user.Id, code = code }, + protocol: Request.Scheme); - await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", - $"Please confirm your account by clicking here."); + await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); - await _signInManager.SignInAsync(user, isPersistent: false); - return LocalRedirect(returnUrl); - } - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } + await _signInManager.SignInAsync(user, isPersistent: false); + return LocalRedirect(returnUrl); + } + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); } - - // If we got this far, something failed, redisplay form - return Page(); } + + // If we got this far, something failed, redisplay form + return Page(); } } diff --git a/src/Web/Configuration/ConfigureCookieSettings.cs b/src/Web/Configuration/ConfigureCookieSettings.cs index 61b9791d2..9462b4b18 100644 --- a/src/Web/Configuration/ConfigureCookieSettings.cs +++ b/src/Web/Configuration/ConfigureCookieSettings.cs @@ -1,41 +1,40 @@ -using Microsoft.AspNetCore.Builder; +using System; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using System; -namespace Microsoft.eShopWeb.Web.Configuration +namespace Microsoft.eShopWeb.Web.Configuration; + +public static class ConfigureCookieSettings { - public static class ConfigureCookieSettings - { - public const int ValidityMinutesPeriod = 60; - public const string IdentifierCookieName = "EshopIdentifier"; + public const int ValidityMinutesPeriod = 60; + public const string IdentifierCookieName = "EshopIdentifier"; - public static IServiceCollection AddCookieSettings(this IServiceCollection services) + public static IServiceCollection AddCookieSettings(this IServiceCollection services) + { + services.Configure(options => { - services.Configure(options => - { // This lambda determines whether user consent for non-essential cookies is needed for a given request. //TODO need to check that. //options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.Strict; - }); - services.ConfigureApplicationCookie(options => + }); + services.ConfigureApplicationCookie(options => + { + options.EventsType = typeof(RevokeAuthenticationEvents); + options.Cookie.HttpOnly = true; + options.ExpireTimeSpan = TimeSpan.FromMinutes(ValidityMinutesPeriod); + options.LoginPath = "/Account/Login"; + options.LogoutPath = "/Account/Logout"; + options.Cookie = new CookieBuilder { - options.EventsType = typeof(RevokeAuthenticationEvents); - options.Cookie.HttpOnly = true; - options.ExpireTimeSpan = TimeSpan.FromMinutes(ValidityMinutesPeriod); - options.LoginPath = "/Account/Login"; - options.LogoutPath = "/Account/Logout"; - options.Cookie = new CookieBuilder - { - Name = IdentifierCookieName, - IsEssential = true // required for auth to work without explicit user consent; adjust to suit your privacy policy + Name = IdentifierCookieName, + IsEssential = true // required for auth to work without explicit user consent; adjust to suit your privacy policy }; - }); + }); - services.AddScoped(); + services.AddScoped(); - return services; - } + return services; } } diff --git a/src/Web/Configuration/ConfigureCoreServices.cs b/src/Web/Configuration/ConfigureCoreServices.cs index 2a2c1c628..33e85ead0 100644 --- a/src/Web/Configuration/ConfigureCoreServices.cs +++ b/src/Web/Configuration/ConfigureCoreServices.cs @@ -6,24 +6,23 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.eShopWeb.Web.Configuration +namespace Microsoft.eShopWeb.Web.Configuration; + +public static class ConfigureCoreServices { - public static class ConfigureCoreServices + public static IServiceCollection AddCoreServices(this IServiceCollection services, + IConfiguration configuration) { - public static IServiceCollection AddCoreServices(this IServiceCollection services, - IConfiguration configuration) - { - services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>)); - services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); + services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>)); + services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddSingleton(new UriComposer(configuration.Get())); - services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>)); - services.AddTransient(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(new UriComposer(configuration.Get())); + services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>)); + services.AddTransient(); - return services; - } + return services; } } diff --git a/src/Web/Configuration/ConfigureWebServices.cs b/src/Web/Configuration/ConfigureWebServices.cs index 87030d548..478b89dd0 100644 --- a/src/Web/Configuration/ConfigureWebServices.cs +++ b/src/Web/Configuration/ConfigureWebServices.cs @@ -4,20 +4,19 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.eShopWeb.Web.Configuration +namespace Microsoft.eShopWeb.Web.Configuration; + +public static class ConfigureWebServices { - public static class ConfigureWebServices + public static IServiceCollection AddWebServices(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddWebServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddMediatR(typeof(BasketViewModelService).Assembly); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.Configure(configuration); - services.AddScoped(); + services.AddMediatR(typeof(BasketViewModelService).Assembly); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.Configure(configuration); + services.AddScoped(); - return services; - } + return services; } } diff --git a/src/Web/Configuration/RevokeAuthenticationEvents.cs b/src/Web/Configuration/RevokeAuthenticationEvents.cs index 641408293..0e2916336 100644 --- a/src/Web/Configuration/RevokeAuthenticationEvents.cs +++ b/src/Web/Configuration/RevokeAuthenticationEvents.cs @@ -1,36 +1,35 @@ -using Microsoft.AspNetCore.Authentication; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Configuration +namespace Microsoft.eShopWeb.Web.Configuration; + +//TODO : replace IMemoryCache with a distributed cache if you are in multi-host scenario +public class RevokeAuthenticationEvents : CookieAuthenticationEvents { - //TODO : replace IMemoryCache with a distributed cache if you are in multi-host scenario - public class RevokeAuthenticationEvents : CookieAuthenticationEvents + private readonly IMemoryCache _cache; + private readonly ILogger _logger; + + public RevokeAuthenticationEvents(IMemoryCache cache, ILogger logger) { - private readonly IMemoryCache _cache; - private readonly ILogger _logger; + _cache = cache; + _logger = logger; + } - public RevokeAuthenticationEvents(IMemoryCache cache, ILogger logger) - { - _cache = cache; - _logger = logger; - } + public override async Task ValidatePrincipal(CookieValidatePrincipalContext context) + { + var userId = context.Principal.Claims.First(c => c.Type == ClaimTypes.Name); + var identityKey = context.Request.Cookies[ConfigureCookieSettings.IdentifierCookieName]; - public override async Task ValidatePrincipal(CookieValidatePrincipalContext context) + if (_cache.TryGetValue($"{userId.Value}:{identityKey}", out var revokeKeys)) { - var userId = context.Principal.Claims.First(c => c.Type == ClaimTypes.Name); - var identityKey = context.Request.Cookies[ConfigureCookieSettings.IdentifierCookieName]; - - if (_cache.TryGetValue($"{userId.Value}:{identityKey}", out var revokeKeys)) - { - _logger.LogDebug($"Access has been revoked for: {userId.Value}."); - context.RejectPrincipal(); - await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - } + _logger.LogDebug($"Access has been revoked for: {userId.Value}."); + context.RejectPrincipal(); + await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); } } } diff --git a/src/Web/Constants.cs b/src/Web/Constants.cs index d383813b8..d093f8e7a 100644 --- a/src/Web/Constants.cs +++ b/src/Web/Constants.cs @@ -1,10 +1,9 @@ - namespace Microsoft.eShopWeb.Web +namespace Microsoft.eShopWeb.Web; + +public static class Constants { - public static class Constants - { - public const string BASKET_COOKIENAME = "eShop"; - public const int ITEMS_PER_PAGE = 10; - public const string DEFAULT_USERNAME = "Guest"; - public const string BASKET_ID = "BasketId"; - } + public const string BASKET_COOKIENAME = "eShop"; + public const int ITEMS_PER_PAGE = 10; + public const string DEFAULT_USERNAME = "Guest"; + public const string BASKET_ID = "BasketId"; } diff --git a/src/Web/Controllers/Api/BaseApiController.cs b/src/Web/Controllers/Api/BaseApiController.cs index 79fb63470..0deee44eb 100644 --- a/src/Web/Controllers/Api/BaseApiController.cs +++ b/src/Web/Controllers/Api/BaseApiController.cs @@ -1,10 +1,9 @@ using Microsoft.AspNetCore.Mvc; -namespace Microsoft.eShopWeb.Web.Controllers.Api -{ - // No longer used - shown for reference only if using full controllers instead of Endpoints for APIs - [Route("api/[controller]/[action]")] - [ApiController] - public class BaseApiController : ControllerBase - { } -} +namespace Microsoft.eShopWeb.Web.Controllers.Api; + +// No longer used - shown for reference only if using full controllers instead of Endpoints for APIs +[Route("api/[controller]/[action]")] +[ApiController] +public class BaseApiController : ControllerBase +{ } diff --git a/src/Web/Controllers/ManageController.cs b/src/Web/Controllers/ManageController.cs index c03f50773..f9d4cb1d9 100644 --- a/src/Web/Controllers/ManageController.cs +++ b/src/Web/Controllers/ManageController.cs @@ -1,4 +1,9 @@ -using Microsoft.AspNetCore.Authentication; +using System; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -6,495 +11,489 @@ using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.eShopWeb.Web.Services; using Microsoft.eShopWeb.Web.ViewModels.Manage; -using System; -using System.Linq; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Controllers +namespace Microsoft.eShopWeb.Web.Controllers; + +[ApiExplorerSettings(IgnoreApi = true)] +[Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages +[Route("[controller]/[action]")] +public class ManageController : Controller { - [ApiExplorerSettings(IgnoreApi = true)] - [Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages - [Route("[controller]/[action]")] - public class ManageController : Controller + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + private readonly IAppLogger _logger; + private readonly UrlEncoder _urlEncoder; + + private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + + public ManageController( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender, + IAppLogger logger, + UrlEncoder urlEncoder) { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IEmailSender _emailSender; - private readonly IAppLogger _logger; - private readonly UrlEncoder _urlEncoder; + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + _logger = logger; + _urlEncoder = urlEncoder; + } - private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + [TempData] + public string StatusMessage { get; set; } - public ManageController( - UserManager userManager, - SignInManager signInManager, - IEmailSender emailSender, - IAppLogger logger, - UrlEncoder urlEncoder) + [HttpGet] + public async Task MyAccount() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) { - _userManager = userManager; - _signInManager = signInManager; - _emailSender = emailSender; - _logger = logger; - _urlEncoder = urlEncoder; + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [TempData] - public string StatusMessage { get; set; } - - [HttpGet] - public async Task MyAccount() + var model = new IndexViewModel { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var model = new IndexViewModel - { - Username = user.UserName, - Email = user.Email, - PhoneNumber = user.PhoneNumber, - IsEmailConfirmed = user.EmailConfirmed, - StatusMessage = StatusMessage - }; + Username = user.UserName, + Email = user.Email, + PhoneNumber = user.PhoneNumber, + IsEmailConfirmed = user.EmailConfirmed, + StatusMessage = StatusMessage + }; + + return View(model); + } + [HttpPost] + [ValidateAntiForgeryToken] + public async Task MyAccount(IndexViewModel model) + { + if (!ModelState.IsValid) + { return View(model); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task MyAccount(IndexViewModel model) + var user = await _userManager.GetUserAsync(User); + if (user == null) { - if (!ModelState.IsValid) - { - return View(model); - } + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } - var user = await _userManager.GetUserAsync(User); - if (user == null) + var email = user.Email; + if (model.Email != email) + { + var setEmailResult = await _userManager.SetEmailAsync(user, model.Email); + if (!setEmailResult.Succeeded) { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + throw new ApplicationException($"Unexpected error occurred setting email for user with ID '{user.Id}'."); } + } - var email = user.Email; - if (model.Email != email) + var phoneNumber = user.PhoneNumber; + if (model.PhoneNumber != phoneNumber) + { + var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, model.PhoneNumber); + if (!setPhoneResult.Succeeded) { - var setEmailResult = await _userManager.SetEmailAsync(user, model.Email); - if (!setEmailResult.Succeeded) - { - throw new ApplicationException($"Unexpected error occurred setting email for user with ID '{user.Id}'."); - } + throw new ApplicationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'."); } + } - var phoneNumber = user.PhoneNumber; - if (model.PhoneNumber != phoneNumber) - { - var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, model.PhoneNumber); - if (!setPhoneResult.Succeeded) - { - throw new ApplicationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'."); - } - } + StatusMessage = "Your profile has been updated"; + return RedirectToAction(nameof(MyAccount)); + } - StatusMessage = "Your profile has been updated"; - return RedirectToAction(nameof(MyAccount)); + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SendVerificationEmail(IndexViewModel model) + { + if (!ModelState.IsValid) + { + return View(model); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task SendVerificationEmail(IndexViewModel model) + var user = await _userManager.GetUserAsync(User); + if (user == null) { - if (!ModelState.IsValid) - { - return View(model); - } + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme); + var email = user.Email; + await _emailSender.SendEmailConfirmationAsync(email, callbackUrl); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme); - var email = user.Email; - await _emailSender.SendEmailConfirmationAsync(email, callbackUrl); + StatusMessage = "Verification email sent. Please check your email."; + return RedirectToAction(nameof(MyAccount)); + } - StatusMessage = "Verification email sent. Please check your email."; - return RedirectToAction(nameof(MyAccount)); + [HttpGet] + public async Task ChangePassword() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task ChangePassword() + var hasPassword = await _userManager.HasPasswordAsync(user); + if (!hasPassword) { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + return RedirectToAction(nameof(SetPassword)); + } - var hasPassword = await _userManager.HasPasswordAsync(user); - if (!hasPassword) - { - return RedirectToAction(nameof(SetPassword)); - } + var model = new ChangePasswordViewModel { StatusMessage = StatusMessage }; + return View(model); + } - var model = new ChangePasswordViewModel { StatusMessage = StatusMessage }; + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ChangePassword(ChangePasswordViewModel model) + { + if (!ModelState.IsValid) + { return View(model); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task ChangePassword(ChangePasswordViewModel model) + var user = await _userManager.GetUserAsync(User); + if (user == null) { - if (!ModelState.IsValid) - { - return View(model); - } + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + var changePasswordResult = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); + if (!changePasswordResult.Succeeded) + { + AddErrors(changePasswordResult); + return View(model); + } - var changePasswordResult = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); - if (!changePasswordResult.Succeeded) - { - AddErrors(changePasswordResult); - return View(model); - } + await _signInManager.SignInAsync(user, isPersistent: false); + _logger.LogInformation("User changed their password successfully."); + StatusMessage = "Your password has been changed."; - await _signInManager.SignInAsync(user, isPersistent: false); - _logger.LogInformation("User changed their password successfully."); - StatusMessage = "Your password has been changed."; + return RedirectToAction(nameof(ChangePassword)); + } - return RedirectToAction(nameof(ChangePassword)); + [HttpGet] + public async Task SetPassword() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task SetPassword() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + var hasPassword = await _userManager.HasPasswordAsync(user); - var hasPassword = await _userManager.HasPasswordAsync(user); + if (hasPassword) + { + return RedirectToAction(nameof(ChangePassword)); + } - if (hasPassword) - { - return RedirectToAction(nameof(ChangePassword)); - } + var model = new SetPasswordViewModel { StatusMessage = StatusMessage }; + return View(model); + } - var model = new SetPasswordViewModel { StatusMessage = StatusMessage }; + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SetPassword(SetPasswordViewModel model) + { + if (!ModelState.IsValid) + { return View(model); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task SetPassword(SetPasswordViewModel model) + var user = await _userManager.GetUserAsync(User); + if (user == null) { - if (!ModelState.IsValid) - { - return View(model); - } + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + var addPasswordResult = await _userManager.AddPasswordAsync(user, model.NewPassword); + if (!addPasswordResult.Succeeded) + { + AddErrors(addPasswordResult); + return View(model); + } - var addPasswordResult = await _userManager.AddPasswordAsync(user, model.NewPassword); - if (!addPasswordResult.Succeeded) - { - AddErrors(addPasswordResult); - return View(model); - } + await _signInManager.SignInAsync(user, isPersistent: false); + StatusMessage = "Your password has been set."; - await _signInManager.SignInAsync(user, isPersistent: false); - StatusMessage = "Your password has been set."; + return RedirectToAction(nameof(SetPassword)); + } - return RedirectToAction(nameof(SetPassword)); + [HttpGet] + public async Task ExternalLogins() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task ExternalLogins() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + var model = new ExternalLoginsViewModel { CurrentLogins = await _userManager.GetLoginsAsync(user) }; + model.OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) + .Where(auth => model.CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) + .ToList(); + model.ShowRemoveButton = await _userManager.HasPasswordAsync(user) || model.CurrentLogins.Count > 1; + model.StatusMessage = StatusMessage; - var model = new ExternalLoginsViewModel { CurrentLogins = await _userManager.GetLoginsAsync(user) }; - model.OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) - .Where(auth => model.CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) - .ToList(); - model.ShowRemoveButton = await _userManager.HasPasswordAsync(user) || model.CurrentLogins.Count > 1; - model.StatusMessage = StatusMessage; + return View(model); + } - return View(model); - } + [HttpPost] + [ValidateAntiForgeryToken] + public async Task LinkLogin(string provider) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - [HttpPost] - [ValidateAntiForgeryToken] - public async Task LinkLogin(string provider) - { - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + // Request a redirect to the external login provider to link a login for the current user + var redirectUrl = Url.Action(nameof(LinkLoginCallback)); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); + return new ChallengeResult(provider, properties); + } - // Request a redirect to the external login provider to link a login for the current user - var redirectUrl = Url.Action(nameof(LinkLoginCallback)); - var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); - return new ChallengeResult(provider, properties); + [HttpGet] + public async Task LinkLoginCallback() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task LinkLoginCallback() + var info = await _signInManager.GetExternalLoginInfoAsync(user.Id); + if (info == null) { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + throw new ApplicationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'."); + } - var info = await _signInManager.GetExternalLoginInfoAsync(user.Id); - if (info == null) - { - throw new ApplicationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'."); - } + var result = await _userManager.AddLoginAsync(user, info); + if (!result.Succeeded) + { + throw new ApplicationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'."); + } - var result = await _userManager.AddLoginAsync(user, info); - if (!result.Succeeded) - { - throw new ApplicationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'."); - } + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + StatusMessage = "The external login was added."; + return RedirectToAction(nameof(ExternalLogins)); + } - StatusMessage = "The external login was added."; - return RedirectToAction(nameof(ExternalLogins)); + [HttpPost] + [ValidateAntiForgeryToken] + public async Task RemoveLogin(RemoveLoginViewModel model) + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task RemoveLogin(RemoveLoginViewModel model) + var result = await _userManager.RemoveLoginAsync(user, model.LoginProvider, model.ProviderKey); + if (!result.Succeeded) { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + throw new ApplicationException($"Unexpected error occurred removing external login for user with ID '{user.Id}'."); + } - var result = await _userManager.RemoveLoginAsync(user, model.LoginProvider, model.ProviderKey); - if (!result.Succeeded) - { - throw new ApplicationException($"Unexpected error occurred removing external login for user with ID '{user.Id}'."); - } + await _signInManager.SignInAsync(user, isPersistent: false); + StatusMessage = "The external login was removed."; + return RedirectToAction(nameof(ExternalLogins)); + } - await _signInManager.SignInAsync(user, isPersistent: false); - StatusMessage = "The external login was removed."; - return RedirectToAction(nameof(ExternalLogins)); + [HttpGet] + public async Task TwoFactorAuthentication() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task TwoFactorAuthentication() + var model = new TwoFactorAuthenticationViewModel { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null, + Is2faEnabled = user.TwoFactorEnabled, + RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user), + }; - var model = new TwoFactorAuthenticationViewModel - { - HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null, - Is2faEnabled = user.TwoFactorEnabled, - RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user), - }; + return View(model); + } - return View(model); + [HttpGet] + public async Task Disable2faWarning() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task Disable2faWarning() + if (!user.TwoFactorEnabled) { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'."); + } - if (!user.TwoFactorEnabled) - { - throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'."); - } + return View(nameof(Disable2fa)); + } - return View(nameof(Disable2fa)); + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Disable2fa() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Disable2fa() + var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); + if (!disable2faResult.Succeeded) { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'."); + } - var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); - if (!disable2faResult.Succeeded) - { - throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'."); - } + _logger.LogInformation("User with ID {UserId} has disabled 2fa.", user.Id); + return RedirectToAction(nameof(TwoFactorAuthentication)); + } - _logger.LogInformation("User with ID {UserId} has disabled 2fa.", user.Id); - return RedirectToAction(nameof(TwoFactorAuthentication)); + [HttpGet] + public async Task EnableAuthenticator() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task EnableAuthenticator() + var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + if (string.IsNullOrEmpty(unformattedKey)) { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + await _userManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + } - var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); - if (string.IsNullOrEmpty(unformattedKey)) - { - await _userManager.ResetAuthenticatorKeyAsync(user); - unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); - } + var model = new EnableAuthenticatorViewModel + { + SharedKey = FormatKey(unformattedKey), + AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey) + }; - var model = new EnableAuthenticatorViewModel - { - SharedKey = FormatKey(unformattedKey), - AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey) - }; + return View(model); + } + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EnableAuthenticator(EnableAuthenticatorViewModel model) + { + if (!ModelState.IsValid) + { return View(model); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EnableAuthenticator(EnableAuthenticatorViewModel model) + var user = await _userManager.GetUserAsync(User); + if (user == null) { - if (!ModelState.IsValid) - { - return View(model); - } + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + // Strip spaces and hypens + var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty); - // Strip spaces and hypens - var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( + user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); - var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( - user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); + if (!is2faTokenValid) + { + ModelState.AddModelError("model.TwoFactorCode", "Verification code is invalid."); + return View(model); + } - if (!is2faTokenValid) - { - ModelState.AddModelError("model.TwoFactorCode", "Verification code is invalid."); - return View(model); - } + await _userManager.SetTwoFactorEnabledAsync(user, true); + _logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id); + return RedirectToAction(nameof(GenerateRecoveryCodes)); + } - await _userManager.SetTwoFactorEnabledAsync(user, true); - _logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id); - return RedirectToAction(nameof(GenerateRecoveryCodes)); - } + [HttpGet] + public IActionResult ResetAuthenticatorWarning() + { + return View(nameof(ResetAuthenticator)); + } - [HttpGet] - public IActionResult ResetAuthenticatorWarning() + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ResetAuthenticator() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) { - return View(nameof(ResetAuthenticator)); + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpPost] - [ValidateAntiForgeryToken] - public async Task ResetAuthenticator() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } + await _userManager.SetTwoFactorEnabledAsync(user, false); + await _userManager.ResetAuthenticatorKeyAsync(user); + _logger.LogInformation("User with id '{UserId}' has reset their authentication app key.", user.Id); - await _userManager.SetTwoFactorEnabledAsync(user, false); - await _userManager.ResetAuthenticatorKeyAsync(user); - _logger.LogInformation("User with id '{UserId}' has reset their authentication app key.", user.Id); + return RedirectToAction(nameof(EnableAuthenticator)); + } - return RedirectToAction(nameof(EnableAuthenticator)); + [HttpGet] + public async Task GenerateRecoveryCodes() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - [HttpGet] - public async Task GenerateRecoveryCodes() + if (!user.TwoFactorEnabled) { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!user.TwoFactorEnabled) - { - throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled."); - } + throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled."); + } - var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); - var model = new GenerateRecoveryCodesViewModel { RecoveryCodes = recoveryCodes.ToArray() }; + var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + var model = new GenerateRecoveryCodesViewModel { RecoveryCodes = recoveryCodes.ToArray() }; - _logger.LogInformation("User with ID {UserId} has generated new 2FA recovery codes.", user.Id); + _logger.LogInformation("User with ID {UserId} has generated new 2FA recovery codes.", user.Id); - return View(model); - } + return View(model); + } - private void AddErrors(IdentityResult result) + private void AddErrors(IdentityResult result) + { + foreach (var error in result.Errors) { - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } + ModelState.AddModelError(string.Empty, error.Description); } + } - private string FormatKey(string unformattedKey) + private string FormatKey(string unformattedKey) + { + var result = new StringBuilder(); + int currentPosition = 0; + while (currentPosition + 4 < unformattedKey.Length) { - var result = new StringBuilder(); - int currentPosition = 0; - while (currentPosition + 4 < unformattedKey.Length) - { - result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); - currentPosition += 4; - } - if (currentPosition < unformattedKey.Length) - { - result.Append(unformattedKey.Substring(currentPosition)); - } - - return result.ToString().ToLowerInvariant(); + result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); + currentPosition += 4; } - - private string GenerateQrCodeUri(string email, string unformattedKey) + if (currentPosition < unformattedKey.Length) { - return string.Format( - AuthenticatorUriFormat, - _urlEncoder.Encode("eShopOnWeb"), - _urlEncoder.Encode(email), - unformattedKey); + result.Append(unformattedKey.Substring(currentPosition)); } + + return result.ToString().ToLowerInvariant(); + } + + private string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format( + AuthenticatorUriFormat, + _urlEncoder.Encode("eShopOnWeb"), + _urlEncoder.Encode(email), + unformattedKey); } } diff --git a/src/Web/Controllers/OrderController.cs b/src/Web/Controllers/OrderController.cs index aa5b36a36..ce6d6d01a 100644 --- a/src/Web/Controllers/OrderController.cs +++ b/src/Web/Controllers/OrderController.cs @@ -1,43 +1,42 @@ -using MediatR; +using System.Threading.Tasks; +using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.eShopWeb.Web.Features.MyOrders; using Microsoft.eShopWeb.Web.Features.OrderDetails; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Controllers +namespace Microsoft.eShopWeb.Web.Controllers; + +[ApiExplorerSettings(IgnoreApi = true)] +[Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages +[Route("[controller]/[action]")] +public class OrderController : Controller { - [ApiExplorerSettings(IgnoreApi = true)] - [Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages - [Route("[controller]/[action]")] - public class OrderController : Controller + private readonly IMediator _mediator; + + public OrderController(IMediator mediator) { - private readonly IMediator _mediator; + _mediator = mediator; + } - public OrderController(IMediator mediator) - { - _mediator = mediator; - } + [HttpGet] + public async Task MyOrders() + { + var viewModel = await _mediator.Send(new GetMyOrders(User.Identity.Name)); - [HttpGet] - public async Task MyOrders() - { - var viewModel = await _mediator.Send(new GetMyOrders(User.Identity.Name)); + return View(viewModel); + } - return View(viewModel); - } + [HttpGet("{orderId}")] + public async Task Detail(int orderId) + { + var viewModel = await _mediator.Send(new GetOrderDetails(User.Identity.Name, orderId)); - [HttpGet("{orderId}")] - public async Task Detail(int orderId) + if (viewModel == null) { - var viewModel = await _mediator.Send(new GetOrderDetails(User.Identity.Name, orderId)); - - if (viewModel == null) - { - return BadRequest("No such order found for this user."); - } - - return View(viewModel); + return BadRequest("No such order found for this user."); } + + return View(viewModel); } } diff --git a/src/Web/Controllers/UserController.cs b/src/Web/Controllers/UserController.cs index 37c34a73f..4945ee273 100644 --- a/src/Web/Controllers/UserController.cs +++ b/src/Web/Controllers/UserController.cs @@ -1,75 +1,74 @@ -using BlazorShared.Authorization; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using BlazorShared.Authorization; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; + +namespace Microsoft.eShopWeb.Web.Controllers; -namespace Microsoft.eShopWeb.Web.Controllers +[Route("[controller]")] +[ApiController] +public class UserController : ControllerBase { - [Route("[controller]")] - [ApiController] - public class UserController : ControllerBase + private readonly ITokenClaimsService _tokenClaimsService; + + public UserController(ITokenClaimsService tokenClaimsService) { - private readonly ITokenClaimsService _tokenClaimsService; + _tokenClaimsService = tokenClaimsService; + } - public UserController(ITokenClaimsService tokenClaimsService) + [HttpGet] + [Authorize] + [AllowAnonymous] + public async Task GetCurrentUser() => + Ok(User.Identity.IsAuthenticated ? await CreateUserInfo(User) : UserInfo.Anonymous); + + private async Task CreateUserInfo(ClaimsPrincipal claimsPrincipal) + { + if (!claimsPrincipal.Identity.IsAuthenticated) { - _tokenClaimsService = tokenClaimsService; + return UserInfo.Anonymous; } - [HttpGet] - [Authorize] - [AllowAnonymous] - public async Task GetCurrentUser() => - Ok(User.Identity.IsAuthenticated ? await CreateUserInfo(User) : UserInfo.Anonymous); - - private async Task CreateUserInfo(ClaimsPrincipal claimsPrincipal) + var userInfo = new UserInfo { - if (!claimsPrincipal.Identity.IsAuthenticated) - { - return UserInfo.Anonymous; - } + IsAuthenticated = true + }; - var userInfo = new UserInfo - { - IsAuthenticated = true - }; + if (claimsPrincipal.Identity is ClaimsIdentity claimsIdentity) + { + userInfo.NameClaimType = claimsIdentity.NameClaimType; + userInfo.RoleClaimType = claimsIdentity.RoleClaimType; + } + else + { + userInfo.NameClaimType = "name"; + userInfo.RoleClaimType = "role"; + } - if (claimsPrincipal.Identity is ClaimsIdentity claimsIdentity) - { - userInfo.NameClaimType = claimsIdentity.NameClaimType; - userInfo.RoleClaimType = claimsIdentity.RoleClaimType; - } - else + if (claimsPrincipal.Claims.Any()) + { + var claims = new List(); + var nameClaims = claimsPrincipal.FindAll(userInfo.NameClaimType); + foreach (var claim in nameClaims) { - userInfo.NameClaimType = "name"; - userInfo.RoleClaimType = "role"; + claims.Add(new ClaimValue(userInfo.NameClaimType, claim.Value)); } - if (claimsPrincipal.Claims.Any()) + foreach (var claim in claimsPrincipal.Claims.Except(nameClaims)) { - var claims = new List(); - var nameClaims = claimsPrincipal.FindAll(userInfo.NameClaimType); - foreach (var claim in nameClaims) - { - claims.Add(new ClaimValue(userInfo.NameClaimType, claim.Value)); - } - - foreach (var claim in claimsPrincipal.Claims.Except(nameClaims)) - { - claims.Add(new ClaimValue(claim.Type, claim.Value)); - } - - userInfo.Claims = claims; + claims.Add(new ClaimValue(claim.Type, claim.Value)); } - var token = await _tokenClaimsService.GetTokenAsync(claimsPrincipal.Identity.Name); - userInfo.Token = token; - - return userInfo; + userInfo.Claims = claims; } + + var token = await _tokenClaimsService.GetTokenAsync(claimsPrincipal.Identity.Name); + userInfo.Token = token; + + return userInfo; } -} \ No newline at end of file +} diff --git a/src/Web/Extensions/CacheHelpers.cs b/src/Web/Extensions/CacheHelpers.cs index e8d92f41e..9f0ed90f9 100644 --- a/src/Web/Extensions/CacheHelpers.cs +++ b/src/Web/Extensions/CacheHelpers.cs @@ -1,25 +1,24 @@ using System; -namespace Microsoft.eShopWeb.Web.Extensions +namespace Microsoft.eShopWeb.Web.Extensions; + +public static class CacheHelpers { - public static class CacheHelpers - { - public static readonly TimeSpan DefaultCacheDuration = TimeSpan.FromSeconds(30); - private static readonly string _itemsKeyTemplate = "items-{0}-{1}-{2}-{3}"; + public static readonly TimeSpan DefaultCacheDuration = TimeSpan.FromSeconds(30); + private static readonly string _itemsKeyTemplate = "items-{0}-{1}-{2}-{3}"; - public static string GenerateCatalogItemCacheKey(int pageIndex, int itemsPage, int? brandId, int? typeId) - { - return string.Format(_itemsKeyTemplate, pageIndex, itemsPage, brandId, typeId); - } + public static string GenerateCatalogItemCacheKey(int pageIndex, int itemsPage, int? brandId, int? typeId) + { + return string.Format(_itemsKeyTemplate, pageIndex, itemsPage, brandId, typeId); + } - public static string GenerateBrandsCacheKey() - { - return "brands"; - } + public static string GenerateBrandsCacheKey() + { + return "brands"; + } - public static string GenerateTypesCacheKey() - { - return "types"; - } + public static string GenerateTypesCacheKey() + { + return "types"; } } diff --git a/src/Web/Extensions/EmailSenderExtensions.cs b/src/Web/Extensions/EmailSenderExtensions.cs index b3bb93cf1..0fb67e491 100644 --- a/src/Web/Extensions/EmailSenderExtensions.cs +++ b/src/Web/Extensions/EmailSenderExtensions.cs @@ -1,15 +1,14 @@ -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using System.Text.Encodings.Web; +using System.Text.Encodings.Web; using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Interfaces; -namespace Microsoft.eShopWeb.Web.Services +namespace Microsoft.eShopWeb.Web.Services; + +public static class EmailSenderExtensions { - public static class EmailSenderExtensions + public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link) { - public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link) - { - return emailSender.SendEmailAsync(email, "Confirm your email", - $"Please confirm your account by clicking this link: link"); - } + return emailSender.SendEmailAsync(email, "Confirm your email", + $"Please confirm your account by clicking this link: link"); } } diff --git a/src/Web/Extensions/UrlHelperExtensions.cs b/src/Web/Extensions/UrlHelperExtensions.cs index 6edc697f8..75ea5988c 100644 --- a/src/Web/Extensions/UrlHelperExtensions.cs +++ b/src/Web/Extensions/UrlHelperExtensions.cs @@ -1,14 +1,13 @@ -namespace Microsoft.AspNetCore.Mvc +namespace Microsoft.AspNetCore.Mvc; + +public static class UrlHelperExtensions { - public static class UrlHelperExtensions + public static string EmailConfirmationLink(this IUrlHelper urlHelper, string userId, string code, string scheme) { - public static string EmailConfirmationLink(this IUrlHelper urlHelper, string userId, string code, string scheme) - { - return urlHelper.Action( - action: "GET", - controller: "ConfirmEmail", - values: new { userId, code }, - protocol: scheme); - } + return urlHelper.Action( + action: "GET", + controller: "ConfirmEmail", + values: new { userId, code }, + protocol: scheme); } } diff --git a/src/Web/Features/MyOrders/GetMyOrders.cs b/src/Web/Features/MyOrders/GetMyOrders.cs index b99a9da94..aedfde159 100644 --- a/src/Web/Features/MyOrders/GetMyOrders.cs +++ b/src/Web/Features/MyOrders/GetMyOrders.cs @@ -1,16 +1,15 @@ -using MediatR; +using System.Collections.Generic; +using MediatR; using Microsoft.eShopWeb.Web.ViewModels; -using System.Collections.Generic; -namespace Microsoft.eShopWeb.Web.Features.MyOrders +namespace Microsoft.eShopWeb.Web.Features.MyOrders; + +public class GetMyOrders : IRequest> { - public class GetMyOrders : IRequest> - { - public string UserName { get; set; } + public string UserName { get; set; } - public GetMyOrders(string userName) - { - UserName = userName; - } + public GetMyOrders(string userName) + { + UserName = userName; } } diff --git a/src/Web/Features/MyOrders/GetMyOrdersHandler.cs b/src/Web/Features/MyOrders/GetMyOrdersHandler.cs index f78bfae1d..a22961ffa 100644 --- a/src/Web/Features/MyOrders/GetMyOrdersHandler.cs +++ b/src/Web/Features/MyOrders/GetMyOrdersHandler.cs @@ -1,45 +1,44 @@ -using MediatR; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediatR; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.Web.ViewModels; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Features.MyOrders +namespace Microsoft.eShopWeb.Web.Features.MyOrders; + +public class GetMyOrdersHandler : IRequestHandler> { - public class GetMyOrdersHandler : IRequestHandler> + private readonly IReadRepository _orderRepository; + + public GetMyOrdersHandler(IReadRepository orderRepository) { - private readonly IReadRepository _orderRepository; + _orderRepository = orderRepository; + } - public GetMyOrdersHandler(IReadRepository orderRepository) - { - _orderRepository = orderRepository; - } + public async Task> Handle(GetMyOrders request, + CancellationToken cancellationToken) + { + var specification = new CustomerOrdersWithItemsSpecification(request.UserName); + var orders = await _orderRepository.ListAsync(specification, cancellationToken); - public async Task> Handle(GetMyOrders request, - CancellationToken cancellationToken) + return orders.Select(o => new OrderViewModel { - var specification = new CustomerOrdersWithItemsSpecification(request.UserName); - var orders = await _orderRepository.ListAsync(specification, cancellationToken); - - return orders.Select(o => new OrderViewModel + OrderDate = o.OrderDate, + OrderItems = o.OrderItems?.Select(oi => new OrderItemViewModel() { - OrderDate = o.OrderDate, - OrderItems = o.OrderItems?.Select(oi => new OrderItemViewModel() - { - PictureUrl = oi.ItemOrdered.PictureUri, - ProductId = oi.ItemOrdered.CatalogItemId, - ProductName = oi.ItemOrdered.ProductName, - UnitPrice = oi.UnitPrice, - Units = oi.Units - }).ToList(), - OrderNumber = o.Id, - ShippingAddress = o.ShipToAddress, - Total = o.Total() - }); - } + PictureUrl = oi.ItemOrdered.PictureUri, + ProductId = oi.ItemOrdered.CatalogItemId, + ProductName = oi.ItemOrdered.ProductName, + UnitPrice = oi.UnitPrice, + Units = oi.Units + }).ToList(), + OrderNumber = o.Id, + ShippingAddress = o.ShipToAddress, + Total = o.Total() + }); } } diff --git a/src/Web/Features/OrderDetails/GetOrderDetails.cs b/src/Web/Features/OrderDetails/GetOrderDetails.cs index 4f36ff2a7..2cc072131 100644 --- a/src/Web/Features/OrderDetails/GetOrderDetails.cs +++ b/src/Web/Features/OrderDetails/GetOrderDetails.cs @@ -1,17 +1,16 @@ using MediatR; using Microsoft.eShopWeb.Web.ViewModels; -namespace Microsoft.eShopWeb.Web.Features.OrderDetails +namespace Microsoft.eShopWeb.Web.Features.OrderDetails; + +public class GetOrderDetails : IRequest { - public class GetOrderDetails : IRequest - { - public string UserName { get; set; } - public int OrderId { get; set; } + public string UserName { get; set; } + public int OrderId { get; set; } - public GetOrderDetails(string userName, int orderId) - { - UserName = userName; - OrderId = orderId; - } + public GetOrderDetails(string userName, int orderId) + { + UserName = userName; + OrderId = orderId; } } diff --git a/src/Web/Features/OrderDetails/GetOrderDetailsHandler.cs b/src/Web/Features/OrderDetails/GetOrderDetailsHandler.cs index 44c270bac..4fa7e54cf 100644 --- a/src/Web/Features/OrderDetails/GetOrderDetailsHandler.cs +++ b/src/Web/Features/OrderDetails/GetOrderDetailsHandler.cs @@ -1,49 +1,48 @@ -using MediatR; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediatR; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.Web.ViewModels; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Features.OrderDetails +namespace Microsoft.eShopWeb.Web.Features.OrderDetails; + +public class GetOrderDetailsHandler : IRequestHandler { - public class GetOrderDetailsHandler : IRequestHandler + private readonly IReadRepository _orderRepository; + + public GetOrderDetailsHandler(IReadRepository orderRepository) { - private readonly IReadRepository _orderRepository; + _orderRepository = orderRepository; + } - public GetOrderDetailsHandler(IReadRepository orderRepository) + public async Task Handle(GetOrderDetails request, + CancellationToken cancellationToken) + { + var spec = new OrderWithItemsByIdSpec(request.OrderId); + var order = await _orderRepository.GetBySpecAsync(spec, cancellationToken); + + if (order == null) { - _orderRepository = orderRepository; + return null; } - public async Task Handle(GetOrderDetails request, - CancellationToken cancellationToken) + return new OrderViewModel { - var spec = new OrderWithItemsByIdSpec(request.OrderId); - var order = await _orderRepository.GetBySpecAsync(spec, cancellationToken); - - if (order == null) + OrderDate = order.OrderDate, + OrderItems = order.OrderItems.Select(oi => new OrderItemViewModel { - return null; - } - - return new OrderViewModel - { - OrderDate = order.OrderDate, - OrderItems = order.OrderItems.Select(oi => new OrderItemViewModel - { - PictureUrl = oi.ItemOrdered.PictureUri, - ProductId = oi.ItemOrdered.CatalogItemId, - ProductName = oi.ItemOrdered.ProductName, - UnitPrice = oi.UnitPrice, - Units = oi.Units - }).ToList(), - OrderNumber = order.Id, - ShippingAddress = order.ShipToAddress, - Total = order.Total() - }; - } + PictureUrl = oi.ItemOrdered.PictureUri, + ProductId = oi.ItemOrdered.CatalogItemId, + ProductName = oi.ItemOrdered.ProductName, + UnitPrice = oi.UnitPrice, + Units = oi.Units + }).ToList(), + OrderNumber = order.Id, + ShippingAddress = order.ShipToAddress, + Total = order.Total() + }; } } diff --git a/src/Web/HealthChecks/ApiHealthCheck.cs b/src/Web/HealthChecks/ApiHealthCheck.cs index 989fe990a..9522eb07f 100644 --- a/src/Web/HealthChecks/ApiHealthCheck.cs +++ b/src/Web/HealthChecks/ApiHealthCheck.cs @@ -1,40 +1,39 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using System.Net.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Diagnostics.HealthChecks; -namespace Microsoft.eShopWeb.Web.HealthChecks +namespace Microsoft.eShopWeb.Web.HealthChecks; + +public class ApiHealthCheck : IHealthCheck { - public class ApiHealthCheck : IHealthCheck + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly LinkGenerator _linkGenerator; + + public ApiHealthCheck(IHttpContextAccessor httpContextAccessor, LinkGenerator linkGenerator) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly LinkGenerator _linkGenerator; + _httpContextAccessor = httpContextAccessor; + _linkGenerator = linkGenerator; + } - public ApiHealthCheck(IHttpContextAccessor httpContextAccessor, LinkGenerator linkGenerator) - { - _httpContextAccessor = httpContextAccessor; - _linkGenerator = linkGenerator; - } + public async Task CheckHealthAsync( + HealthCheckContext context, + CancellationToken cancellationToken = default(CancellationToken)) + { + var request = _httpContextAccessor.HttpContext.Request; - public async Task CheckHealthAsync( - HealthCheckContext context, - CancellationToken cancellationToken = default(CancellationToken)) + string apiLink = _linkGenerator.GetPathByAction("List", "Catalog"); + string myUrl = request.Scheme + "://" + request.Host.ToString() + apiLink; + var client = new HttpClient(); + var response = await client.GetAsync(myUrl); + var pageContents = await response.Content.ReadAsStringAsync(); + if (pageContents.Contains(".NET Bot Black Sweatshirt")) { - var request = _httpContextAccessor.HttpContext.Request; - - string apiLink = _linkGenerator.GetPathByAction("List", "Catalog"); - string myUrl = request.Scheme + "://" + request.Host.ToString() + apiLink; - var client = new HttpClient(); - var response = await client.GetAsync(myUrl); - var pageContents = await response.Content.ReadAsStringAsync(); - if (pageContents.Contains(".NET Bot Black Sweatshirt")) - { - return HealthCheckResult.Healthy("The check indicates a healthy result."); - } - - return HealthCheckResult.Unhealthy("The check indicates an unhealthy result."); + return HealthCheckResult.Healthy("The check indicates a healthy result."); } + + return HealthCheckResult.Unhealthy("The check indicates an unhealthy result."); } } diff --git a/src/Web/HealthChecks/HomePageHealthCheck.cs b/src/Web/HealthChecks/HomePageHealthCheck.cs index f858a55ae..0579dd78e 100644 --- a/src/Web/HealthChecks/HomePageHealthCheck.cs +++ b/src/Web/HealthChecks/HomePageHealthCheck.cs @@ -1,36 +1,35 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using System.Net.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Microsoft.eShopWeb.Web.HealthChecks; -namespace Microsoft.eShopWeb.Web.HealthChecks +public class HomePageHealthCheck : IHealthCheck { - public class HomePageHealthCheck : IHealthCheck + private readonly IHttpContextAccessor _httpContextAccessor; + + public HomePageHealthCheck(IHttpContextAccessor httpContextAccessor) { - private readonly IHttpContextAccessor _httpContextAccessor; + _httpContextAccessor = httpContextAccessor; + } - public HomePageHealthCheck(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } + public async Task CheckHealthAsync( + HealthCheckContext context, + CancellationToken cancellationToken = default(CancellationToken)) + { + var request = _httpContextAccessor.HttpContext.Request; + string myUrl = request.Scheme + "://" + request.Host.ToString(); - public async Task CheckHealthAsync( - HealthCheckContext context, - CancellationToken cancellationToken = default(CancellationToken)) + var client = new HttpClient(); + var response = await client.GetAsync(myUrl); + var pageContents = await response.Content.ReadAsStringAsync(); + if (pageContents.Contains(".NET Bot Black Sweatshirt")) { - var request = _httpContextAccessor.HttpContext.Request; - string myUrl = request.Scheme + "://" + request.Host.ToString(); - - var client = new HttpClient(); - var response = await client.GetAsync(myUrl); - var pageContents = await response.Content.ReadAsStringAsync(); - if (pageContents.Contains(".NET Bot Black Sweatshirt")) - { - return HealthCheckResult.Healthy("The check indicates a healthy result."); - } - - return HealthCheckResult.Unhealthy("The check indicates an unhealthy result."); + return HealthCheckResult.Healthy("The check indicates a healthy result."); } + + return HealthCheckResult.Unhealthy("The check indicates an unhealthy result."); } } diff --git a/src/Web/Interfaces/IBasketViewModelService.cs b/src/Web/Interfaces/IBasketViewModelService.cs index 82e4aadbe..0c18a2545 100644 --- a/src/Web/Interfaces/IBasketViewModelService.cs +++ b/src/Web/Interfaces/IBasketViewModelService.cs @@ -1,15 +1,14 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.Web.Pages.Basket; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Interfaces +namespace Microsoft.eShopWeb.Web.Interfaces; + +public interface IBasketViewModelService { - public interface IBasketViewModelService - { - Task GetOrCreateBasketForUser(string userName); + Task GetOrCreateBasketForUser(string userName); - Task CountTotalBasketItems(string username); + Task CountTotalBasketItems(string username); - Task Map(Basket basket); - } + Task Map(Basket basket); } diff --git a/src/Web/Interfaces/ICatalogItemViewModelService.cs b/src/Web/Interfaces/ICatalogItemViewModelService.cs index 00b7f2648..ede5902e8 100644 --- a/src/Web/Interfaces/ICatalogItemViewModelService.cs +++ b/src/Web/Interfaces/ICatalogItemViewModelService.cs @@ -1,10 +1,9 @@ -using Microsoft.eShopWeb.Web.ViewModels; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.eShopWeb.Web.ViewModels; -namespace Microsoft.eShopWeb.Web.Interfaces +namespace Microsoft.eShopWeb.Web.Interfaces; + +public interface ICatalogItemViewModelService { - public interface ICatalogItemViewModelService - { - Task UpdateCatalogItem(CatalogItemViewModel viewModel); - } + Task UpdateCatalogItem(CatalogItemViewModel viewModel); } diff --git a/src/Web/Interfaces/ICatalogViewModelService.cs b/src/Web/Interfaces/ICatalogViewModelService.cs index a229bde4b..338f83dfc 100644 --- a/src/Web/Interfaces/ICatalogViewModelService.cs +++ b/src/Web/Interfaces/ICatalogViewModelService.cs @@ -1,14 +1,13 @@ -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.eShopWeb.Web.ViewModels; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.eShopWeb.Web.ViewModels; + +namespace Microsoft.eShopWeb.Web.Services; -namespace Microsoft.eShopWeb.Web.Services +public interface ICatalogViewModelService { - public interface ICatalogViewModelService - { - Task GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId); - Task> GetBrands(); - Task> GetTypes(); - } + Task GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId); + Task> GetBrands(); + Task> GetTypes(); } diff --git a/src/Web/Pages/Admin/EditCatalogItem.cshtml.cs b/src/Web/Pages/Admin/EditCatalogItem.cshtml.cs index 686114db9..7cd25693b 100644 --- a/src/Web/Pages/Admin/EditCatalogItem.cshtml.cs +++ b/src/Web/Pages/Admin/EditCatalogItem.cshtml.cs @@ -1,39 +1,38 @@ -using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.eShopWeb.ApplicationCore.Constants; using Microsoft.eShopWeb.Web.Interfaces; using Microsoft.eShopWeb.Web.ViewModels; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Pages.Admin +namespace Microsoft.eShopWeb.Web.Pages.Admin; + +[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)] +public class EditCatalogItemModel : PageModel { - [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)] - public class EditCatalogItemModel : PageModel + private readonly ICatalogItemViewModelService _catalogItemViewModelService; + + public EditCatalogItemModel(ICatalogItemViewModelService catalogItemViewModelService) { - private readonly ICatalogItemViewModelService _catalogItemViewModelService; + _catalogItemViewModelService = catalogItemViewModelService; + } - public EditCatalogItemModel(ICatalogItemViewModelService catalogItemViewModelService) - { - _catalogItemViewModelService = catalogItemViewModelService; - } + [BindProperty] + public CatalogItemViewModel CatalogModel { get; set; } = new CatalogItemViewModel(); - [BindProperty] - public CatalogItemViewModel CatalogModel { get; set; } = new CatalogItemViewModel(); + public void OnGet(CatalogItemViewModel catalogModel) + { + CatalogModel = catalogModel; + } - public void OnGet(CatalogItemViewModel catalogModel) + public async Task OnPostAsync() + { + if (ModelState.IsValid) { - CatalogModel = catalogModel; + await _catalogItemViewModelService.UpdateCatalogItem(CatalogModel); } - public async Task OnPostAsync() - { - if (ModelState.IsValid) - { - await _catalogItemViewModelService.UpdateCatalogItem(CatalogModel); - } - - return RedirectToPage("/Admin/Index"); - } + return RedirectToPage("/Admin/Index"); } } diff --git a/src/Web/Pages/Admin/Index.cshtml.cs b/src/Web/Pages/Admin/Index.cshtml.cs index 52a961763..8dfe0b9fb 100644 --- a/src/Web/Pages/Admin/Index.cshtml.cs +++ b/src/Web/Pages/Admin/Index.cshtml.cs @@ -1,20 +1,19 @@ -using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.eShopWeb.ApplicationCore.Constants; using Microsoft.eShopWeb.Web.Extensions; using Microsoft.eShopWeb.Web.Services; using Microsoft.eShopWeb.Web.ViewModels; using Microsoft.Extensions.Caching.Memory; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Pages.Admin +namespace Microsoft.eShopWeb.Web.Pages.Admin; + +[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)] +public class IndexModel : PageModel { - [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)] - public class IndexModel : PageModel + public IndexModel() { - public IndexModel() - { - - } + } } diff --git a/src/Web/Pages/Basket/BasketItemViewModel.cs b/src/Web/Pages/Basket/BasketItemViewModel.cs index a30a60899..b8f65f509 100644 --- a/src/Web/Pages/Basket/BasketItemViewModel.cs +++ b/src/Web/Pages/Basket/BasketItemViewModel.cs @@ -1,18 +1,17 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.Pages.Basket +namespace Microsoft.eShopWeb.Web.Pages.Basket; + +public class BasketItemViewModel { - public class BasketItemViewModel - { - public int Id { get; set; } - public int CatalogItemId { get; set; } - public string ProductName { get; set; } - public decimal UnitPrice { get; set; } - public decimal OldUnitPrice { get; set; } + public int Id { get; set; } + public int CatalogItemId { get; set; } + public string ProductName { get; set; } + public decimal UnitPrice { get; set; } + public decimal OldUnitPrice { get; set; } + + [Range(0, int.MaxValue, ErrorMessage = "Quantity must be bigger than 0")] + public int Quantity { get; set; } - [Range(0, int.MaxValue, ErrorMessage = "Quantity must be bigger than 0")] - public int Quantity { get; set; } - - public string PictureUrl { get; set; } - } + public string PictureUrl { get; set; } } diff --git a/src/Web/Pages/Basket/BasketViewModel.cs b/src/Web/Pages/Basket/BasketViewModel.cs index 264df2b61..7b76e40b6 100644 --- a/src/Web/Pages/Basket/BasketViewModel.cs +++ b/src/Web/Pages/Basket/BasketViewModel.cs @@ -2,17 +2,16 @@ using System.Collections.Generic; using System.Linq; -namespace Microsoft.eShopWeb.Web.Pages.Basket +namespace Microsoft.eShopWeb.Web.Pages.Basket; + +public class BasketViewModel { - public class BasketViewModel - { - public int Id { get; set; } - public List Items { get; set; } = new List(); - public string BuyerId { get; set; } + public int Id { get; set; } + public List Items { get; set; } = new List(); + public string BuyerId { get; set; } - public decimal Total() - { - return Math.Round(Items.Sum(x => x.UnitPrice * x.Quantity), 2); - } + public decimal Total() + { + return Math.Round(Items.Sum(x => x.UnitPrice * x.Quantity), 2); } } diff --git a/src/Web/Pages/Basket/Checkout.cshtml.cs b/src/Web/Pages/Basket/Checkout.cshtml.cs index 6a9402ee7..be616891b 100644 --- a/src/Web/Pages/Basket/Checkout.cshtml.cs +++ b/src/Web/Pages/Basket/Checkout.cshtml.cs @@ -1,4 +1,8 @@ -using Microsoft.AspNetCore.Authentication.Cookies; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; @@ -9,94 +13,89 @@ using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.eShopWeb.Web.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Pages.Basket +namespace Microsoft.eShopWeb.Web.Pages.Basket; + +[Authorize] +public class CheckoutModel : PageModel { - [Authorize] - public class CheckoutModel : PageModel + private readonly IBasketService _basketService; + private readonly SignInManager _signInManager; + private readonly IOrderService _orderService; + private string _username = null; + private readonly IBasketViewModelService _basketViewModelService; + private readonly IAppLogger _logger; + + public CheckoutModel(IBasketService basketService, + IBasketViewModelService basketViewModelService, + SignInManager signInManager, + IOrderService orderService, + IAppLogger logger) { - private readonly IBasketService _basketService; - private readonly SignInManager _signInManager; - private readonly IOrderService _orderService; - private string _username = null; - private readonly IBasketViewModelService _basketViewModelService; - private readonly IAppLogger _logger; + _basketService = basketService; + _signInManager = signInManager; + _orderService = orderService; + _basketViewModelService = basketViewModelService; + _logger = logger; + } - public CheckoutModel(IBasketService basketService, - IBasketViewModelService basketViewModelService, - SignInManager signInManager, - IOrderService orderService, - IAppLogger logger) - { - _basketService = basketService; - _signInManager = signInManager; - _orderService = orderService; - _basketViewModelService = basketViewModelService; - _logger = logger; - } + public BasketViewModel BasketModel { get; set; } = new BasketViewModel(); - public BasketViewModel BasketModel { get; set; } = new BasketViewModel(); + public async Task OnGet() + { + await SetBasketModelAsync(); + } - public async Task OnGet() + public async Task OnPost(IEnumerable items) + { + try { await SetBasketModelAsync(); - } - public async Task OnPost(IEnumerable items) - { - try + if (!ModelState.IsValid) { - await SetBasketModelAsync(); - - if (!ModelState.IsValid) - { - return BadRequest(); - } - - var updateModel = items.ToDictionary(b => b.Id.ToString(), b => b.Quantity); - await _basketService.SetQuantities(BasketModel.Id, updateModel); - await _orderService.CreateOrderAsync(BasketModel.Id, new Address("123 Main St.", "Kent", "OH", "United States", "44240")); - await _basketService.DeleteBasketAsync(BasketModel.Id); - } - catch (EmptyBasketOnCheckoutException emptyBasketOnCheckoutException) - { - //Redirect to Empty Basket page - _logger.LogWarning(emptyBasketOnCheckoutException.Message); - return RedirectToPage("/Basket/Index"); + return BadRequest(); } - return RedirectToPage("Success"); + var updateModel = items.ToDictionary(b => b.Id.ToString(), b => b.Quantity); + await _basketService.SetQuantities(BasketModel.Id, updateModel); + await _orderService.CreateOrderAsync(BasketModel.Id, new Address("123 Main St.", "Kent", "OH", "United States", "44240")); + await _basketService.DeleteBasketAsync(BasketModel.Id); } - - private async Task SetBasketModelAsync() + catch (EmptyBasketOnCheckoutException emptyBasketOnCheckoutException) { - if (_signInManager.IsSignedIn(HttpContext.User)) - { - BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name); - } - else - { - GetOrSetBasketCookieAndUserName(); - BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(_username); - } + //Redirect to Empty Basket page + _logger.LogWarning(emptyBasketOnCheckoutException.Message); + return RedirectToPage("/Basket/Index"); } - private void GetOrSetBasketCookieAndUserName() + return RedirectToPage("Success"); + } + + private async Task SetBasketModelAsync() + { + if (_signInManager.IsSignedIn(HttpContext.User)) { - if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) - { - _username = Request.Cookies[Constants.BASKET_COOKIENAME]; - } - if (_username != null) return; + BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name); + } + else + { + GetOrSetBasketCookieAndUserName(); + BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(_username); + } + } - _username = Guid.NewGuid().ToString(); - var cookieOptions = new CookieOptions(); - cookieOptions.Expires = DateTime.Today.AddYears(10); - Response.Cookies.Append(Constants.BASKET_COOKIENAME, _username, cookieOptions); + private void GetOrSetBasketCookieAndUserName() + { + if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) + { + _username = Request.Cookies[Constants.BASKET_COOKIENAME]; } + if (_username != null) return; + + _username = Guid.NewGuid().ToString(); + var cookieOptions = new CookieOptions(); + cookieOptions.Expires = DateTime.Today.AddYears(10); + Response.Cookies.Append(Constants.BASKET_COOKIENAME, _username, cookieOptions); } } diff --git a/src/Web/Pages/Basket/Index.cshtml.cs b/src/Web/Pages/Basket/Index.cshtml.cs index bf7417298..ad42bf784 100644 --- a/src/Web/Pages/Basket/Index.cshtml.cs +++ b/src/Web/Pages/Basket/Index.cshtml.cs @@ -1,93 +1,92 @@ -using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.Web.Interfaces; using Microsoft.eShopWeb.Web.ViewModels; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Pages.Basket +namespace Microsoft.eShopWeb.Web.Pages.Basket; + +public class IndexModel : PageModel { - public class IndexModel : PageModel + private readonly IBasketService _basketService; + private readonly IBasketViewModelService _basketViewModelService; + + public IndexModel(IBasketService basketService, + IBasketViewModelService basketViewModelService) { - private readonly IBasketService _basketService; - private readonly IBasketViewModelService _basketViewModelService; + _basketService = basketService; + _basketViewModelService = basketViewModelService; + } - public IndexModel(IBasketService basketService, - IBasketViewModelService basketViewModelService) - { - _basketService = basketService; - _basketViewModelService = basketViewModelService; - } + public BasketViewModel BasketModel { get; set; } = new BasketViewModel(); - public BasketViewModel BasketModel { get; set; } = new BasketViewModel(); + public async Task OnGet() + { + BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(GetOrSetBasketCookieAndUserName()); + } - public async Task OnGet() + public async Task OnPost(CatalogItemViewModel productDetails) + { + if (productDetails?.Id == null) { - BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(GetOrSetBasketCookieAndUserName()); + return RedirectToPage("/Index"); } - public async Task OnPost(CatalogItemViewModel productDetails) - { - if (productDetails?.Id == null) - { - return RedirectToPage("/Index"); - } + var username = GetOrSetBasketCookieAndUserName(); + var basket = await _basketService.AddItemToBasket(username, + productDetails.Id, productDetails.Price); - var username = GetOrSetBasketCookieAndUserName(); - var basket = await _basketService.AddItemToBasket(username, - productDetails.Id, productDetails.Price); + BasketModel = await _basketViewModelService.Map(basket); - BasketModel = await _basketViewModelService.Map(basket); + return RedirectToPage(); + } - return RedirectToPage(); + public async Task OnPostUpdate(IEnumerable items) + { + if (!ModelState.IsValid) + { + return; } - public async Task OnPostUpdate(IEnumerable items) - { - if (!ModelState.IsValid) - { - return; - } + var basketView = await _basketViewModelService.GetOrCreateBasketForUser(GetOrSetBasketCookieAndUserName()); + var updateModel = items.ToDictionary(b => b.Id.ToString(), b => b.Quantity); + var basket = await _basketService.SetQuantities(basketView.Id, updateModel); + BasketModel = await _basketViewModelService.Map(basket); + } - var basketView = await _basketViewModelService.GetOrCreateBasketForUser(GetOrSetBasketCookieAndUserName()); - var updateModel = items.ToDictionary(b => b.Id.ToString(), b => b.Quantity); - var basket = await _basketService.SetQuantities(basketView.Id, updateModel); - BasketModel = await _basketViewModelService.Map(basket); - } + private string GetOrSetBasketCookieAndUserName() + { + string userName = null; - private string GetOrSetBasketCookieAndUserName() + if (Request.HttpContext.User.Identity.IsAuthenticated) { - string userName = null; + return Request.HttpContext.User.Identity.Name; + } - if (Request.HttpContext.User.Identity.IsAuthenticated) - { - return Request.HttpContext.User.Identity.Name; - } + if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) + { + userName = Request.Cookies[Constants.BASKET_COOKIENAME]; - if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) + if (!Request.HttpContext.User.Identity.IsAuthenticated) { - userName = Request.Cookies[Constants.BASKET_COOKIENAME]; - - if (!Request.HttpContext.User.Identity.IsAuthenticated) + if (!Guid.TryParse(userName, out var _)) { - if (!Guid.TryParse(userName, out var _)) - { - userName = null; - } + userName = null; } } - if (userName != null) return userName; + } + if (userName != null) return userName; - userName = Guid.NewGuid().ToString(); - var cookieOptions = new CookieOptions { IsEssential = true }; - cookieOptions.Expires = DateTime.Today.AddYears(10); - Response.Cookies.Append(Constants.BASKET_COOKIENAME, userName, cookieOptions); + userName = Guid.NewGuid().ToString(); + var cookieOptions = new CookieOptions { IsEssential = true }; + cookieOptions.Expires = DateTime.Today.AddYears(10); + Response.Cookies.Append(Constants.BASKET_COOKIENAME, userName, cookieOptions); - return userName; - } + return userName; } } diff --git a/src/Web/Pages/Basket/Success.cshtml.cs b/src/Web/Pages/Basket/Success.cshtml.cs index 3f1882f4d..e105aa38e 100644 --- a/src/Web/Pages/Basket/Success.cshtml.cs +++ b/src/Web/Pages/Basket/Success.cshtml.cs @@ -6,14 +6,13 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -namespace Microsoft.eShopWeb.Web.Pages.Basket +namespace Microsoft.eShopWeb.Web.Pages.Basket; + +[Authorize] +public class SuccessModel : PageModel { - [Authorize] - public class SuccessModel : PageModel + public void OnGet() { - public void OnGet() - { - } } -} \ No newline at end of file +} diff --git a/src/Web/Pages/Error.cshtml.cs b/src/Web/Pages/Error.cshtml.cs index 5ec6f2e3b..532e5a972 100644 --- a/src/Web/Pages/Error.cshtml.cs +++ b/src/Web/Pages/Error.cshtml.cs @@ -1,19 +1,18 @@ -using Microsoft.AspNetCore.Mvc; +using System.Diagnostics; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using System.Diagnostics; -namespace Microsoft.eShopWeb.Web.Pages +namespace Microsoft.eShopWeb.Web.Pages; + +[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] +public class ErrorModel : PageModel { - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public class ErrorModel : PageModel - { - public string RequestId { get; set; } + public string RequestId { get; set; } - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } + public void OnGet() + { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; } } diff --git a/src/Web/Pages/Index.cshtml.cs b/src/Web/Pages/Index.cshtml.cs index 8fc4fe787..d70c613f7 100644 --- a/src/Web/Pages/Index.cshtml.cs +++ b/src/Web/Pages/Index.cshtml.cs @@ -1,24 +1,23 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.eShopWeb.Web.Services; using Microsoft.eShopWeb.Web.ViewModels; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Pages +namespace Microsoft.eShopWeb.Web.Pages; + +public class IndexModel : PageModel { - public class IndexModel : PageModel - { - private readonly ICatalogViewModelService _catalogViewModelService; + private readonly ICatalogViewModelService _catalogViewModelService; - public IndexModel(ICatalogViewModelService catalogViewModelService) - { - _catalogViewModelService = catalogViewModelService; - } + public IndexModel(ICatalogViewModelService catalogViewModelService) + { + _catalogViewModelService = catalogViewModelService; + } - public CatalogIndexViewModel CatalogModel { get; set; } = new CatalogIndexViewModel(); + public CatalogIndexViewModel CatalogModel { get; set; } = new CatalogIndexViewModel(); - public async Task OnGet(CatalogIndexViewModel catalogModel, int? pageId) - { - CatalogModel = await _catalogViewModelService.GetCatalogItems(pageId ?? 0, Constants.ITEMS_PER_PAGE, catalogModel.BrandFilterApplied, catalogModel.TypesFilterApplied); - } + public async Task OnGet(CatalogIndexViewModel catalogModel, int? pageId) + { + CatalogModel = await _catalogViewModelService.GetCatalogItems(pageId ?? 0, Constants.ITEMS_PER_PAGE, catalogModel.BrandFilterApplied, catalogModel.TypesFilterApplied); } } diff --git a/src/Web/Pages/Privacy.cshtml.cs b/src/Web/Pages/Privacy.cshtml.cs index b3f4e5885..616a9441c 100644 --- a/src/Web/Pages/Privacy.cshtml.cs +++ b/src/Web/Pages/Privacy.cshtml.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace Microsoft.eShopWeb.Web.Pages +namespace Microsoft.eShopWeb.Web.Pages; + +public class PrivacyModel : PageModel { - public class PrivacyModel : PageModel + public void OnGet() { - public void OnGet() - { - } } } diff --git a/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs b/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs index 69541647e..519a335ea 100644 --- a/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs +++ b/src/Web/Pages/Shared/Components/BasketComponent/Basket.cs @@ -1,60 +1,59 @@ -using Microsoft.AspNetCore.Identity; +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.eShopWeb.Infrastructure.Identity; using Microsoft.eShopWeb.Web.Interfaces; using Microsoft.eShopWeb.Web.ViewModels; -using System; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent +namespace Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent; + +public class Basket : ViewComponent { - public class Basket : ViewComponent + private readonly IBasketViewModelService _basketService; + private readonly SignInManager _signInManager; + + public Basket(IBasketViewModelService basketService, + SignInManager signInManager) { - private readonly IBasketViewModelService _basketService; - private readonly SignInManager _signInManager; + _basketService = basketService; + _signInManager = signInManager; + } - public Basket(IBasketViewModelService basketService, - SignInManager signInManager) + public async Task InvokeAsync() + { + var vm = new BasketComponentViewModel { - _basketService = basketService; - _signInManager = signInManager; - } + ItemsCount = await CountTotalBasketItems() + }; + return View(vm); + } - public async Task InvokeAsync() + private async Task CountTotalBasketItems() + { + if (_signInManager.IsSignedIn(HttpContext.User)) { - var vm = new BasketComponentViewModel - { - ItemsCount = await CountTotalBasketItems() - }; - return View(vm); + return await _basketService.CountTotalBasketItems(User.Identity.Name); } - private async Task CountTotalBasketItems() - { - if (_signInManager.IsSignedIn(HttpContext.User)) - { - return await _basketService.CountTotalBasketItems(User.Identity.Name); - } + string anonymousId = GetAnnonymousIdFromCookie(); + if (anonymousId == null) + return 0; - string anonymousId = GetAnnonymousIdFromCookie(); - if (anonymousId == null) - return 0; - - return await _basketService.CountTotalBasketItems(anonymousId); - } + return await _basketService.CountTotalBasketItems(anonymousId); + } - private string GetAnnonymousIdFromCookie() + private string GetAnnonymousIdFromCookie() + { + if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) { - if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) - { - var id = Request.Cookies[Constants.BASKET_COOKIENAME]; + var id = Request.Cookies[Constants.BASKET_COOKIENAME]; - if (Guid.TryParse(id, out var _)) - { - return id; - } + if (Guid.TryParse(id, out var _)) + { + return id; } - return null; } + return null; } } diff --git a/src/Web/Program.cs b/src/Web/Program.cs index 7eb569b75..6455a29b6 100644 --- a/src/Web/Program.cs +++ b/src/Web/Program.cs @@ -1,50 +1,49 @@ -using Microsoft.AspNetCore.Hosting; +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.eShopWeb.Infrastructure.Data; 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.Web +namespace Microsoft.eShopWeb.Web; + +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(); + var host = CreateHostBuilder(args) + .Build(); - using (var scope = host.Services.CreateScope()) + using (var scope = host.Services.CreateScope()) + { + var services = scope.ServiceProvider; + var loggerFactory = services.GetRequiredService(); + try { - var services = scope.ServiceProvider; - var loggerFactory = services.GetRequiredService(); - try - { - var catalogContext = services.GetRequiredService(); - await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory); + var catalogContext = services.GetRequiredService(); + await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory); - var userManager = services.GetRequiredService>(); - var roleManager = services.GetRequiredService>(); - await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); - } - catch (Exception ex) - { - var logger = loggerFactory.CreateLogger(); - logger.LogError(ex, "An error occurred seeding the DB."); - } + var userManager = services.GetRequiredService>(); + var roleManager = services.GetRequiredService>(); + await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); + } + catch (Exception ex) + { + var logger = loggerFactory.CreateLogger(); + logger.LogError(ex, "An error occurred seeding the DB."); } - - host.Run(); } - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + host.Run(); } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } diff --git a/src/Web/Services/BasketViewModelService.cs b/src/Web/Services/BasketViewModelService.cs index 310cac7d0..06cfdf4c6 100644 --- a/src/Web/Services/BasketViewModelService.cs +++ b/src/Web/Services/BasketViewModelService.cs @@ -1,97 +1,96 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.Web.Interfaces; using Microsoft.eShopWeb.Web.Pages.Basket; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Services +namespace Microsoft.eShopWeb.Web.Services; + +public class BasketViewModelService : IBasketViewModelService { - public class BasketViewModelService : IBasketViewModelService + private readonly IRepository _basketRepository; + private readonly IUriComposer _uriComposer; + private readonly IBasketQueryService _basketQueryService; + private readonly IRepository _itemRepository; + + public BasketViewModelService(IRepository basketRepository, + IRepository itemRepository, + IUriComposer uriComposer, + IBasketQueryService basketQueryService) { - private readonly IRepository _basketRepository; - private readonly IUriComposer _uriComposer; - private readonly IBasketQueryService _basketQueryService; - private readonly IRepository _itemRepository; + _basketRepository = basketRepository; + _uriComposer = uriComposer; + _basketQueryService = basketQueryService; + _itemRepository = itemRepository; + } - public BasketViewModelService(IRepository basketRepository, - IRepository itemRepository, - IUriComposer uriComposer, - IBasketQueryService basketQueryService) - { - _basketRepository = basketRepository; - _uriComposer = uriComposer; - _basketQueryService = basketQueryService; - _itemRepository = itemRepository; - } + public async Task GetOrCreateBasketForUser(string userName) + { + var basketSpec = new BasketWithItemsSpecification(userName); + var basket = (await _basketRepository.GetBySpecAsync(basketSpec)); - public async Task GetOrCreateBasketForUser(string userName) + if (basket == null) { - var basketSpec = new BasketWithItemsSpecification(userName); - var basket = (await _basketRepository.GetBySpecAsync(basketSpec)); - - if (basket == null) - { - return await CreateBasketForUser(userName); - } - var viewModel = await Map(basket); - return viewModel; + return await CreateBasketForUser(userName); } + var viewModel = await Map(basket); + return viewModel; + } + + private async Task CreateBasketForUser(string userId) + { + var basket = new Basket(userId); + await _basketRepository.AddAsync(basket); - private async Task CreateBasketForUser(string userId) + return new BasketViewModel() { - var basket = new Basket(userId); - await _basketRepository.AddAsync(basket); + BuyerId = basket.BuyerId, + Id = basket.Id, + }; + } - return new BasketViewModel() - { - BuyerId = basket.BuyerId, - Id = basket.Id, - }; - } + private async Task> GetBasketItems(IReadOnlyCollection basketItems) + { + var catalogItemsSpecification = new CatalogItemsSpecification(basketItems.Select(b => b.CatalogItemId).ToArray()); + var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification); - private async Task> GetBasketItems(IReadOnlyCollection basketItems) + var items = basketItems.Select(basketItem => { - var catalogItemsSpecification = new CatalogItemsSpecification(basketItems.Select(b => b.CatalogItemId).ToArray()); - var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification); + var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId); - var items = basketItems.Select(basketItem => + var basketItemViewModel = new BasketItemViewModel { - var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId); - - var basketItemViewModel = new BasketItemViewModel - { - Id = basketItem.Id, - UnitPrice = basketItem.UnitPrice, - Quantity = basketItem.Quantity, - CatalogItemId = basketItem.CatalogItemId, - PictureUrl = _uriComposer.ComposePicUri(catalogItem.PictureUri), - ProductName = catalogItem.Name - }; - return basketItemViewModel; - }).ToList(); + Id = basketItem.Id, + UnitPrice = basketItem.UnitPrice, + Quantity = basketItem.Quantity, + CatalogItemId = basketItem.CatalogItemId, + PictureUrl = _uriComposer.ComposePicUri(catalogItem.PictureUri), + ProductName = catalogItem.Name + }; + return basketItemViewModel; + }).ToList(); - return items; - } + return items; + } - public async Task Map(Basket basket) + public async Task Map(Basket basket) + { + return new BasketViewModel() { - return new BasketViewModel() - { - BuyerId = basket.BuyerId, - Id = basket.Id, - Items = await GetBasketItems(basket.Items) - }; - } + BuyerId = basket.BuyerId, + Id = basket.Id, + Items = await GetBasketItems(basket.Items) + }; + } - public async Task CountTotalBasketItems(string username) - { - var counter = await _basketQueryService.CountTotalBasketItems(username); + public async Task CountTotalBasketItems(string username) + { + var counter = await _basketQueryService.CountTotalBasketItems(username); - return counter; - } + return counter; } } diff --git a/src/Web/Services/CachedCatalogViewModelService.cs b/src/Web/Services/CachedCatalogViewModelService.cs index 36345fa33..d190d4381 100644 --- a/src/Web/Services/CachedCatalogViewModelService.cs +++ b/src/Web/Services/CachedCatalogViewModelService.cs @@ -1,51 +1,50 @@ -using Microsoft.AspNetCore.Mvc.Rendering; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.eShopWeb.Web.Extensions; using Microsoft.eShopWeb.Web.ViewModels; using Microsoft.Extensions.Caching.Memory; -using System.Collections.Generic; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Services +namespace Microsoft.eShopWeb.Web.Services; + +public class CachedCatalogViewModelService : ICatalogViewModelService { - public class CachedCatalogViewModelService : ICatalogViewModelService + private readonly IMemoryCache _cache; + private readonly CatalogViewModelService _catalogViewModelService; + + public CachedCatalogViewModelService(IMemoryCache cache, + CatalogViewModelService catalogViewModelService) { - private readonly IMemoryCache _cache; - private readonly CatalogViewModelService _catalogViewModelService; + _cache = cache; + _catalogViewModelService = catalogViewModelService; + } - public CachedCatalogViewModelService(IMemoryCache cache, - CatalogViewModelService catalogViewModelService) - { - _cache = cache; - _catalogViewModelService = catalogViewModelService; - } + public async Task> GetBrands() + { + return await _cache.GetOrCreateAsync(CacheHelpers.GenerateBrandsCacheKey(), async entry => + { + entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration; + return await _catalogViewModelService.GetBrands(); + }); + } - public async Task> GetBrands() - { - return await _cache.GetOrCreateAsync(CacheHelpers.GenerateBrandsCacheKey(), async entry => - { - entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration; - return await _catalogViewModelService.GetBrands(); - }); - } + public async Task GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId) + { + var cacheKey = CacheHelpers.GenerateCatalogItemCacheKey(pageIndex, Constants.ITEMS_PER_PAGE, brandId, typeId); - public async Task GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId) + return await _cache.GetOrCreateAsync(cacheKey, async entry => { - var cacheKey = CacheHelpers.GenerateCatalogItemCacheKey(pageIndex, Constants.ITEMS_PER_PAGE, brandId, typeId); - - return await _cache.GetOrCreateAsync(cacheKey, async entry => - { - entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration; - return await _catalogViewModelService.GetCatalogItems(pageIndex, itemsPage, brandId, typeId); - }); - } + entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration; + return await _catalogViewModelService.GetCatalogItems(pageIndex, itemsPage, brandId, typeId); + }); + } - public async Task> GetTypes() + public async Task> GetTypes() + { + return await _cache.GetOrCreateAsync(CacheHelpers.GenerateTypesCacheKey(), async entry => { - return await _cache.GetOrCreateAsync(CacheHelpers.GenerateTypesCacheKey(), async entry => - { - entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration; - return await _catalogViewModelService.GetTypes(); - }); - } + entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration; + return await _catalogViewModelService.GetTypes(); + }); } } diff --git a/src/Web/Services/CatalogItemViewModelService.cs b/src/Web/Services/CatalogItemViewModelService.cs index f7bbd751f..7c66d6175 100644 --- a/src/Web/Services/CatalogItemViewModelService.cs +++ b/src/Web/Services/CatalogItemViewModelService.cs @@ -1,25 +1,24 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.Web.Interfaces; using Microsoft.eShopWeb.Web.ViewModels; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Services +namespace Microsoft.eShopWeb.Web.Services; + +public class CatalogItemViewModelService : ICatalogItemViewModelService { - public class CatalogItemViewModelService : ICatalogItemViewModelService - { - private readonly IRepository _catalogItemRepository; + private readonly IRepository _catalogItemRepository; - public CatalogItemViewModelService(IRepository catalogItemRepository) - { - _catalogItemRepository = catalogItemRepository; - } + public CatalogItemViewModelService(IRepository catalogItemRepository) + { + _catalogItemRepository = catalogItemRepository; + } - public async Task UpdateCatalogItem(CatalogItemViewModel viewModel) - { - var existingCatalogItem = await _catalogItemRepository.GetByIdAsync(viewModel.Id); - existingCatalogItem.UpdateDetails(viewModel.Name, existingCatalogItem.Description, viewModel.Price); - await _catalogItemRepository.UpdateAsync(existingCatalogItem); - } + public async Task UpdateCatalogItem(CatalogItemViewModel viewModel) + { + var existingCatalogItem = await _catalogItemRepository.GetByIdAsync(viewModel.Id); + existingCatalogItem.UpdateDetails(viewModel.Name, existingCatalogItem.Description, viewModel.Price); + await _catalogItemRepository.UpdateAsync(existingCatalogItem); } } diff --git a/src/Web/Services/CatalogViewModelService.cs b/src/Web/Services/CatalogViewModelService.cs index b80376487..4b1b21660 100644 --- a/src/Web/Services/CatalogViewModelService.cs +++ b/src/Web/Services/CatalogViewModelService.cs @@ -1,112 +1,111 @@ -using Microsoft.AspNetCore.Mvc.Rendering; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.Web.ViewModels; using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace Microsoft.eShopWeb.Web.Services +namespace Microsoft.eShopWeb.Web.Services; + +/// +/// This is a UI-specific service so belongs in UI project. It does not contain any business logic and works +/// with UI-specific types (view models and SelectListItem types). +/// +public class CatalogViewModelService : ICatalogViewModelService { - /// - /// This is a UI-specific service so belongs in UI project. It does not contain any business logic and works - /// with UI-specific types (view models and SelectListItem types). - /// - public class CatalogViewModelService : ICatalogViewModelService + private readonly ILogger _logger; + private readonly IRepository _itemRepository; + private readonly IRepository _brandRepository; + private readonly IRepository _typeRepository; + private readonly IUriComposer _uriComposer; + + public CatalogViewModelService( + ILoggerFactory loggerFactory, + IRepository itemRepository, + IRepository brandRepository, + IRepository typeRepository, + IUriComposer uriComposer) { - private readonly ILogger _logger; - private readonly IRepository _itemRepository; - private readonly IRepository _brandRepository; - private readonly IRepository _typeRepository; - private readonly IUriComposer _uriComposer; - - public CatalogViewModelService( - ILoggerFactory loggerFactory, - IRepository itemRepository, - IRepository brandRepository, - IRepository typeRepository, - IUriComposer uriComposer) - { - _logger = loggerFactory.CreateLogger(); - _itemRepository = itemRepository; - _brandRepository = brandRepository; - _typeRepository = typeRepository; - _uriComposer = uriComposer; - } - - public async Task GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId) - { - _logger.LogInformation("GetCatalogItems called."); + _logger = loggerFactory.CreateLogger(); + _itemRepository = itemRepository; + _brandRepository = brandRepository; + _typeRepository = typeRepository; + _uriComposer = uriComposer; + } - var filterSpecification = new CatalogFilterSpecification(brandId, typeId); - var filterPaginatedSpecification = - new CatalogFilterPaginatedSpecification(itemsPage * pageIndex, itemsPage, brandId, typeId); + public async Task GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId) + { + _logger.LogInformation("GetCatalogItems called."); - // the implementation below using ForEach and Count. We need a List. - var itemsOnPage = await _itemRepository.ListAsync(filterPaginatedSpecification); - var totalItems = await _itemRepository.CountAsync(filterSpecification); + var filterSpecification = new CatalogFilterSpecification(brandId, typeId); + var filterPaginatedSpecification = + new CatalogFilterPaginatedSpecification(itemsPage * pageIndex, itemsPage, brandId, typeId); - var vm = new CatalogIndexViewModel() - { - CatalogItems = itemsOnPage.Select(i => new CatalogItemViewModel() - { - Id = i.Id, - Name = i.Name, - PictureUri = _uriComposer.ComposePicUri(i.PictureUri), - Price = i.Price - }).ToList(), - Brands = (await GetBrands()).ToList(), - Types = (await GetTypes()).ToList(), - BrandFilterApplied = brandId ?? 0, - TypesFilterApplied = typeId ?? 0, - PaginationInfo = new PaginationInfoViewModel() - { - ActualPage = pageIndex, - ItemsPerPage = itemsOnPage.Count, - TotalItems = totalItems, - TotalPages = int.Parse(Math.Ceiling(((decimal)totalItems / itemsPage)).ToString()) - } - }; - - vm.PaginationInfo.Next = (vm.PaginationInfo.ActualPage == vm.PaginationInfo.TotalPages - 1) ? "is-disabled" : ""; - vm.PaginationInfo.Previous = (vm.PaginationInfo.ActualPage == 0) ? "is-disabled" : ""; - - return vm; - } - - public async Task> GetBrands() + // the implementation below using ForEach and Count. We need a List. + var itemsOnPage = await _itemRepository.ListAsync(filterPaginatedSpecification); + var totalItems = await _itemRepository.CountAsync(filterSpecification); + + var vm = new CatalogIndexViewModel() { - _logger.LogInformation("GetBrands called."); - var brands = await _brandRepository.ListAsync(); + CatalogItems = itemsOnPage.Select(i => new CatalogItemViewModel() + { + Id = i.Id, + Name = i.Name, + PictureUri = _uriComposer.ComposePicUri(i.PictureUri), + Price = i.Price + }).ToList(), + Brands = (await GetBrands()).ToList(), + Types = (await GetTypes()).ToList(), + BrandFilterApplied = brandId ?? 0, + TypesFilterApplied = typeId ?? 0, + PaginationInfo = new PaginationInfoViewModel() + { + ActualPage = pageIndex, + ItemsPerPage = itemsOnPage.Count, + TotalItems = totalItems, + TotalPages = int.Parse(Math.Ceiling(((decimal)totalItems / itemsPage)).ToString()) + } + }; - var items = brands - .Select(brand => new SelectListItem() { Value = brand.Id.ToString(), Text = brand.Brand }) - .OrderBy(b => b.Text) - .ToList(); + vm.PaginationInfo.Next = (vm.PaginationInfo.ActualPage == vm.PaginationInfo.TotalPages - 1) ? "is-disabled" : ""; + vm.PaginationInfo.Previous = (vm.PaginationInfo.ActualPage == 0) ? "is-disabled" : ""; - var allItem = new SelectListItem() { Value = null, Text = "All", Selected = true }; - items.Insert(0, allItem); + return vm; + } - return items; - } + public async Task> GetBrands() + { + _logger.LogInformation("GetBrands called."); + var brands = await _brandRepository.ListAsync(); - public async Task> GetTypes() - { - _logger.LogInformation("GetTypes called."); - var types = await _typeRepository.ListAsync(); + var items = brands + .Select(brand => new SelectListItem() { Value = brand.Id.ToString(), Text = brand.Brand }) + .OrderBy(b => b.Text) + .ToList(); + + var allItem = new SelectListItem() { Value = null, Text = "All", Selected = true }; + items.Insert(0, allItem); + + return items; + } + + public async Task> GetTypes() + { + _logger.LogInformation("GetTypes called."); + var types = await _typeRepository.ListAsync(); - var items = types - .Select(type => new SelectListItem() { Value = type.Id.ToString(), Text = type.Type }) - .OrderBy(t => t.Text) - .ToList(); + var items = types + .Select(type => new SelectListItem() { Value = type.Id.ToString(), Text = type.Type }) + .OrderBy(t => t.Text) + .ToList(); - var allItem = new SelectListItem() { Value = null, Text = "All", Selected = true }; - items.Insert(0, allItem); + var allItem = new SelectListItem() { Value = null, Text = "All", Selected = true }; + items.Insert(0, allItem); - return items; - } + return items; } } diff --git a/src/Web/SlugifyParameterTransformer.cs b/src/Web/SlugifyParameterTransformer.cs index 3987c4f6f..c0e18cd96 100644 --- a/src/Web/SlugifyParameterTransformer.cs +++ b/src/Web/SlugifyParameterTransformer.cs @@ -1,17 +1,15 @@ -using Microsoft.AspNetCore.Routing; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Routing; -namespace Microsoft.eShopWeb.Web -{ +namespace Microsoft.eShopWeb.Web; - public class SlugifyParameterTransformer : IOutboundParameterTransformer +public class SlugifyParameterTransformer : IOutboundParameterTransformer +{ + public string TransformOutbound(object value) { - public string TransformOutbound(object value) - { - if (value == null) { return null; } + if (value == null) { return null; } - // Slugify value - return Regex.Replace(value.ToString(), "([a-z])([A-Z])", "$1-$2").ToLower(); - } + // Slugify value + return Regex.Replace(value.ToString(), "([a-z])([A-Z])", "$1-$2").ToLower(); } } diff --git a/src/Web/Startup.cs b/src/Web/Startup.cs index 49ac3d6ce..541ca5861 100644 --- a/src/Web/Startup.cs +++ b/src/Web/Startup.cs @@ -1,4 +1,10 @@ -using Ardalis.ListStartupServices; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Mime; +using Ardalis.ListStartupServices; using BlazorAdmin; using BlazorAdmin.Services; using Blazored.LocalStorage; @@ -20,220 +26,212 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Net.Mime; -namespace Microsoft.eShopWeb.Web +namespace Microsoft.eShopWeb.Web; + +public class Startup { - public class Startup - { - private IServiceCollection _services; + private IServiceCollection _services; - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - public void ConfigureDevelopmentServices(IServiceCollection services) - { - // use in-memory database - ConfigureInMemoryDatabases(services); + public void ConfigureDevelopmentServices(IServiceCollection services) + { + // use in-memory database + ConfigureInMemoryDatabases(services); - // use real database - //ConfigureProductionServices(services); - } + // use real database + //ConfigureProductionServices(services); + } - public void ConfigureDockerServices(IServiceCollection services) - { - services.AddDataProtection() - .SetApplicationName("eshopwebmvc") - .PersistKeysToFileSystem(new DirectoryInfo(@"./")); + public void ConfigureDockerServices(IServiceCollection services) + { + services.AddDataProtection() + .SetApplicationName("eshopwebmvc") + .PersistKeysToFileSystem(new DirectoryInfo(@"./")); - ConfigureDevelopmentServices(services); - } + ConfigureDevelopmentServices(services); + } - private void ConfigureInMemoryDatabases(IServiceCollection services) - { - // use in-memory database - services.AddDbContext(c => - c.UseInMemoryDatabase("Catalog")); + private void ConfigureInMemoryDatabases(IServiceCollection services) + { + // use in-memory database + services.AddDbContext(c => + c.UseInMemoryDatabase("Catalog")); - // Add Identity DbContext - services.AddDbContext(options => - options.UseInMemoryDatabase("Identity")); + // Add Identity DbContext + services.AddDbContext(options => + options.UseInMemoryDatabase("Identity")); - ConfigureServices(services); - } + 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(c => - c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection"))); + 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(c => + c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection"))); - // Add Identity DbContext - services.AddDbContext(options => - options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection"))); + // Add Identity DbContext + services.AddDbContext(options => + options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection"))); - ConfigureServices(services); - } + ConfigureServices(services); + } - public void ConfigureTestingServices(IServiceCollection services) - { - ConfigureInMemoryDatabases(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.AddCookieSettings(); + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddCookieSettings(); - services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie(options => - { - options.Cookie.HttpOnly = true; - options.Cookie.SecurePolicy = CookieSecurePolicy.Always; - options.Cookie.SameSite = SameSiteMode.Lax; - }); + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(options => + { + options.Cookie.HttpOnly = true; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + options.Cookie.SameSite = SameSiteMode.Lax; + }); - services.AddIdentity() - .AddDefaultUI() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); + services.AddIdentity() + .AddDefaultUI() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); - services.AddScoped(); + services.AddScoped(); - services.AddCoreServices(Configuration); - services.AddWebServices(Configuration); + services.AddCoreServices(Configuration); + services.AddWebServices(Configuration); - // Add memory cache services - services.AddMemoryCache(); - services.AddRouting(options => - { + // Add memory cache services + services.AddMemoryCache(); + services.AddRouting(options => + { // Replace the type and the name used to refer to it with your own // IOutboundParameterTransformer implementation options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer); - }); - services.AddMvc(options => - { - options.Conventions.Add(new RouteTokenTransformerConvention( - new SlugifyParameterTransformer())); + }); + services.AddMvc(options => + { + options.Conventions.Add(new RouteTokenTransformerConvention( + new SlugifyParameterTransformer())); - }); - services.AddControllersWithViews(); - services.AddRazorPages(options => - { - options.Conventions.AuthorizePage("/Basket/Checkout"); - }); - services.AddHttpContextAccessor(); - services.AddHealthChecks(); - services.Configure(config => - { - config.Services = new List(services); + }); + services.AddControllersWithViews(); + services.AddRazorPages(options => + { + options.Conventions.AuthorizePage("/Basket/Checkout"); + }); + services.AddHttpContextAccessor(); + services.AddHealthChecks(); + services.Configure(config => + { + config.Services = new List(services); - config.Path = "/allservices"; - }); + config.Path = "/allservices"; + }); - - var baseUrlConfig = new BaseUrlConfiguration(); - Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); - services.AddScoped(sp => baseUrlConfig); - // Blazor Admin Required Services for Prerendering - services.AddScoped(s => new HttpClient - { - BaseAddress = new Uri(baseUrlConfig.WebBase) - }); - // add blazor services - services.AddBlazoredLocalStorage(); - services.AddServerSideBlazor(); + var baseUrlConfig = new BaseUrlConfiguration(); + Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig); + services.AddScoped(sp => baseUrlConfig); + // Blazor Admin Required Services for Prerendering + services.AddScoped(s => new HttpClient + { + BaseAddress = new Uri(baseUrlConfig.WebBase) + }); + // add blazor services + services.AddBlazoredLocalStorage(); + services.AddServerSideBlazor(); - services.AddScoped(); - services.AddScoped(); - services.AddBlazorServices(); - services.AddDatabaseDeveloperPageExceptionFilter(); + services.AddScoped(); + services.AddScoped(); + services.AddBlazorServices(); - _services = services; // used to debug registered services - } + services.AddDatabaseDeveloperPageExceptionFilter(); + + _services = services; // used to debug registered services + } - // 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) + { + var catalogBaseUrl = Configuration.GetValue(typeof(string), "CatalogBaseUrl") as string; + if (!string.IsNullOrEmpty(catalogBaseUrl)) { - var catalogBaseUrl = Configuration.GetValue(typeof(string), "CatalogBaseUrl") as string; - if (!string.IsNullOrEmpty(catalogBaseUrl)) + app.Use((context, next) => { - app.Use((context, next) => - { - context.Request.PathBase = new PathString(catalogBaseUrl); - return next(); - }); - } + context.Request.PathBase = new PathString(catalogBaseUrl); + return next(); + }); + } - app.UseHealthChecks("/health", - new HealthCheckOptions + app.UseHealthChecks("/health", + new HealthCheckOptions + { + ResponseWriter = async (context, report) => { - ResponseWriter = async (context, report) => + var result = new { - var result = new + status = report.Status.ToString(), + errors = report.Entries.Select(e => new { - status = report.Status.ToString(), - errors = report.Entries.Select(e => new - { - key = e.Key, - value = Enum.GetName(typeof(HealthStatus), e.Value.Status) - }) - }.ToJson(); - context.Response.ContentType = MediaTypeNames.Application.Json; - await context.Response.WriteAsync(result); - } - }); - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseShowAllServicesMiddleware(); - app.UseMigrationsEndPoint(); - app.UseWebAssemblyDebugging(); - } - else - { - app.UseExceptionHandler("/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } + key = e.Key, + value = Enum.GetName(typeof(HealthStatus), e.Value.Status) + }) + }.ToJson(); + context.Response.ContentType = MediaTypeNames.Application.Json; + await context.Response.WriteAsync(result); + } + }); + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseShowAllServicesMiddleware(); + app.UseMigrationsEndPoint(); + app.UseWebAssemblyDebugging(); + } + else + { + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } - app.UseHttpsRedirection(); - app.UseBlazorFrameworkFiles(); - app.UseStaticFiles(); - app.UseRouting(); + app.UseHttpsRedirection(); + app.UseBlazorFrameworkFiles(); + app.UseStaticFiles(); + app.UseRouting(); - app.UseCookiePolicy(); - app.UseAuthentication(); - app.UseAuthorization(); + app.UseCookiePolicy(); + app.UseAuthentication(); + app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute("default", "{controller:slugify=Home}/{action:slugify=Index}/{id?}"); - endpoints.MapRazorPages(); - endpoints.MapHealthChecks("home_page_health_check"); - endpoints.MapHealthChecks("api_health_check"); + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute("default", "{controller:slugify=Home}/{action:slugify=Index}/{id?}"); + endpoints.MapRazorPages(); + endpoints.MapHealthChecks("home_page_health_check"); + endpoints.MapHealthChecks("api_health_check"); //endpoints.MapBlazorHub("/admin"); endpoints.MapFallbackToFile("index.html"); - }); - } - + }); } -} \ No newline at end of file +} diff --git a/src/Web/ViewModels/Account/LoginViewModel.cs b/src/Web/ViewModels/Account/LoginViewModel.cs index 5945e8818..2230bb7d4 100644 --- a/src/Web/ViewModels/Account/LoginViewModel.cs +++ b/src/Web/ViewModels/Account/LoginViewModel.cs @@ -1,18 +1,17 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Account +namespace Microsoft.eShopWeb.Web.ViewModels.Account; + +public class LoginViewModel { - public class LoginViewModel - { - [Required] - [EmailAddress] - public string Email { get; set; } + [Required] + [EmailAddress] + public string Email { get; set; } - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } - [Display(Name = "Remember me?")] - public bool RememberMe { get; set; } - } + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } } diff --git a/src/Web/ViewModels/Account/LoginWith2faViewModel.cs b/src/Web/ViewModels/Account/LoginWith2faViewModel.cs index 24985c864..f09954be1 100644 --- a/src/Web/ViewModels/Account/LoginWith2faViewModel.cs +++ b/src/Web/ViewModels/Account/LoginWith2faViewModel.cs @@ -1,18 +1,17 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Account +namespace Microsoft.eShopWeb.Web.ViewModels.Account; + +public class LoginWith2faViewModel { - public class LoginWith2faViewModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Authenticator code")] - public string TwoFactorCode { get; set; } + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Authenticator code")] + public string TwoFactorCode { get; set; } - [Display(Name = "Remember this machine")] - public bool RememberMachine { get; set; } + [Display(Name = "Remember this machine")] + public bool RememberMachine { get; set; } - public bool RememberMe { get; set; } - } + public bool RememberMe { get; set; } } diff --git a/src/Web/ViewModels/Account/RegisterViewModel.cs b/src/Web/ViewModels/Account/RegisterViewModel.cs index b1bd4f900..f73b17e3d 100644 --- a/src/Web/ViewModels/Account/RegisterViewModel.cs +++ b/src/Web/ViewModels/Account/RegisterViewModel.cs @@ -1,23 +1,22 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Account +namespace Microsoft.eShopWeb.Web.ViewModels.Account; + +public class RegisterViewModel { - public class RegisterViewModel - { - [Required] - [EmailAddress] - [Display(Name = "Email")] - public string Email { get; set; } + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "Password")] - public string Password { get; set; } + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } } diff --git a/src/Web/ViewModels/Account/ResetPasswordViewModel.cs b/src/Web/ViewModels/Account/ResetPasswordViewModel.cs index 49c2ac7bc..6bf3979ab 100644 --- a/src/Web/ViewModels/Account/ResetPasswordViewModel.cs +++ b/src/Web/ViewModels/Account/ResetPasswordViewModel.cs @@ -1,23 +1,22 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Account +namespace Microsoft.eShopWeb.Web.ViewModels.Account; + +public class ResetPasswordViewModel { - public class ResetPasswordViewModel - { - [Required] - [EmailAddress] - public string Email { get; set; } + [Required] + [EmailAddress] + public string Email { get; set; } - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - public string Password { get; set; } + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } - public string Code { get; set; } - } + public string Code { get; set; } } diff --git a/src/Web/ViewModels/BasketComponentViewModel.cs b/src/Web/ViewModels/BasketComponentViewModel.cs index bd9cbccc7..ef111252c 100644 --- a/src/Web/ViewModels/BasketComponentViewModel.cs +++ b/src/Web/ViewModels/BasketComponentViewModel.cs @@ -1,7 +1,6 @@ -namespace Microsoft.eShopWeb.Web.ViewModels +namespace Microsoft.eShopWeb.Web.ViewModels; + +public class BasketComponentViewModel { - public class BasketComponentViewModel - { - public int ItemsCount { get; set; } - } + public int ItemsCount { get; set; } } diff --git a/src/Web/ViewModels/CatalogIndexViewModel.cs b/src/Web/ViewModels/CatalogIndexViewModel.cs index 8a2396575..b3457bd09 100644 --- a/src/Web/ViewModels/CatalogIndexViewModel.cs +++ b/src/Web/ViewModels/CatalogIndexViewModel.cs @@ -1,15 +1,14 @@ -using Microsoft.AspNetCore.Mvc.Rendering; -using System.Collections.Generic; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.Rendering; -namespace Microsoft.eShopWeb.Web.ViewModels +namespace Microsoft.eShopWeb.Web.ViewModels; + +public class CatalogIndexViewModel { - public class CatalogIndexViewModel - { - public List CatalogItems { get; set; } - public List Brands { get; set; } - public List Types { get; set; } - public int? BrandFilterApplied { get; set; } - public int? TypesFilterApplied { get; set; } - public PaginationInfoViewModel PaginationInfo { get; set; } - } + public List CatalogItems { get; set; } + public List Brands { get; set; } + public List Types { get; set; } + public int? BrandFilterApplied { get; set; } + public int? TypesFilterApplied { get; set; } + public PaginationInfoViewModel PaginationInfo { get; set; } } diff --git a/src/Web/ViewModels/CatalogItemViewModel.cs b/src/Web/ViewModels/CatalogItemViewModel.cs index 5105b7276..6b92830c5 100644 --- a/src/Web/ViewModels/CatalogItemViewModel.cs +++ b/src/Web/ViewModels/CatalogItemViewModel.cs @@ -1,10 +1,9 @@ -namespace Microsoft.eShopWeb.Web.ViewModels +namespace Microsoft.eShopWeb.Web.ViewModels; + +public class CatalogItemViewModel { - public class CatalogItemViewModel - { - public int Id { get; set; } - public string Name { get; set; } - public string PictureUri { get; set; } - public decimal Price { get; set; } - } + public int Id { get; set; } + public string Name { get; set; } + public string PictureUri { get; set; } + public decimal Price { get; set; } } diff --git a/src/Web/ViewModels/File/FileViewModel.cs b/src/Web/ViewModels/File/FileViewModel.cs index e139f13ad..bae902889 100644 --- a/src/Web/ViewModels/File/FileViewModel.cs +++ b/src/Web/ViewModels/File/FileViewModel.cs @@ -1,9 +1,8 @@ -namespace Microsoft.eShopWeb.Web.ViewModels.File +namespace Microsoft.eShopWeb.Web.ViewModels.File; + +public class FileViewModel { - public class FileViewModel - { - public string FileName { get; set; } - public string Url { get; set; } - public string DataBase64 { get; set; } - } + public string FileName { get; set; } + public string Url { get; set; } + public string DataBase64 { get; set; } } diff --git a/src/Web/ViewModels/Manage/ChangePasswordViewModel.cs b/src/Web/ViewModels/Manage/ChangePasswordViewModel.cs index 0e32c2396..0739cab15 100644 --- a/src/Web/ViewModels/Manage/ChangePasswordViewModel.cs +++ b/src/Web/ViewModels/Manage/ChangePasswordViewModel.cs @@ -1,25 +1,24 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class ChangePasswordViewModel { - public class ChangePasswordViewModel - { - [Required] - [DataType(DataType.Password)] - [Display(Name = "Current password")] - public string OldPassword { get; set; } + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } - public string StatusMessage { get; set; } - } + public string StatusMessage { get; set; } } diff --git a/src/Web/ViewModels/Manage/EnableAuthenticatorViewModel.cs b/src/Web/ViewModels/Manage/EnableAuthenticatorViewModel.cs index eafeea80b..247504829 100644 --- a/src/Web/ViewModels/Manage/EnableAuthenticatorViewModel.cs +++ b/src/Web/ViewModels/Manage/EnableAuthenticatorViewModel.cs @@ -1,19 +1,18 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class EnableAuthenticatorViewModel { - public class EnableAuthenticatorViewModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Verification Code")] - public string Code { get; set; } + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } - [ReadOnly(true)] - public string SharedKey { get; set; } + [ReadOnly(true)] + public string SharedKey { get; set; } - public string AuthenticatorUri { get; set; } - } + public string AuthenticatorUri { get; set; } } diff --git a/src/Web/ViewModels/Manage/ExternalLoginsViewModel.cs b/src/Web/ViewModels/Manage/ExternalLoginsViewModel.cs index 5b64a81e0..9dfa96461 100644 --- a/src/Web/ViewModels/Manage/ExternalLoginsViewModel.cs +++ b/src/Web/ViewModels/Manage/ExternalLoginsViewModel.cs @@ -1,14 +1,13 @@ -using Microsoft.AspNetCore.Authentication; +using System.Collections.Generic; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; -using System.Collections.Generic; -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class ExternalLoginsViewModel { - public class ExternalLoginsViewModel - { - public IList CurrentLogins { get; set; } - public IList OtherLogins { get; set; } - public bool ShowRemoveButton { get; set; } - public string StatusMessage { get; set; } - } + public IList CurrentLogins { get; set; } + public IList OtherLogins { get; set; } + public bool ShowRemoveButton { get; set; } + public string StatusMessage { get; set; } } diff --git a/src/Web/ViewModels/Manage/GenerateRecoveryCodesViewModel.cs b/src/Web/ViewModels/Manage/GenerateRecoveryCodesViewModel.cs index 0537a8f4f..b130a2557 100644 --- a/src/Web/ViewModels/Manage/GenerateRecoveryCodesViewModel.cs +++ b/src/Web/ViewModels/Manage/GenerateRecoveryCodesViewModel.cs @@ -1,7 +1,6 @@ -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class GenerateRecoveryCodesViewModel { - public class GenerateRecoveryCodesViewModel - { - public string[] RecoveryCodes { get; set; } - } + public string[] RecoveryCodes { get; set; } } diff --git a/src/Web/ViewModels/Manage/IndexViewModel.cs b/src/Web/ViewModels/Manage/IndexViewModel.cs index ac3548688..6be255394 100644 --- a/src/Web/ViewModels/Manage/IndexViewModel.cs +++ b/src/Web/ViewModels/Manage/IndexViewModel.cs @@ -1,21 +1,20 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class IndexViewModel { - public class IndexViewModel - { - public string Username { get; set; } + public string Username { get; set; } - public bool IsEmailConfirmed { get; set; } + public bool IsEmailConfirmed { get; set; } - [Required] - [EmailAddress] - public string Email { get; set; } + [Required] + [EmailAddress] + public string Email { get; set; } - [Phone] - [Display(Name = "Phone number")] - public string PhoneNumber { get; set; } + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } - public string StatusMessage { get; set; } - } + public string StatusMessage { get; set; } } diff --git a/src/Web/ViewModels/Manage/RemoveLoginViewModel.cs b/src/Web/ViewModels/Manage/RemoveLoginViewModel.cs index bf0395d30..2cd871cda 100644 --- a/src/Web/ViewModels/Manage/RemoveLoginViewModel.cs +++ b/src/Web/ViewModels/Manage/RemoveLoginViewModel.cs @@ -1,8 +1,7 @@ -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class RemoveLoginViewModel { - public class RemoveLoginViewModel - { - public string LoginProvider { get; set; } - public string ProviderKey { get; set; } - } + public string LoginProvider { get; set; } + public string ProviderKey { get; set; } } diff --git a/src/Web/ViewModels/Manage/SetPasswordViewModel.cs b/src/Web/ViewModels/Manage/SetPasswordViewModel.cs index 0c2673930..b0c2897f6 100644 --- a/src/Web/ViewModels/Manage/SetPasswordViewModel.cs +++ b/src/Web/ViewModels/Manage/SetPasswordViewModel.cs @@ -1,20 +1,19 @@ using System.ComponentModel.DataAnnotations; -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class SetPasswordViewModel { - public class SetPasswordViewModel - { - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } - public string StatusMessage { get; set; } - } + public string StatusMessage { get; set; } } diff --git a/src/Web/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs b/src/Web/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs index 00a579564..70f0deaf7 100644 --- a/src/Web/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs +++ b/src/Web/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs @@ -1,9 +1,8 @@ -namespace Microsoft.eShopWeb.Web.ViewModels.Manage +namespace Microsoft.eShopWeb.Web.ViewModels.Manage; + +public class TwoFactorAuthenticationViewModel { - public class TwoFactorAuthenticationViewModel - { - public bool HasAuthenticator { get; set; } - public int RecoveryCodesLeft { get; set; } - public bool Is2faEnabled { get; set; } - } + public bool HasAuthenticator { get; set; } + public int RecoveryCodesLeft { get; set; } + public bool Is2faEnabled { get; set; } } diff --git a/src/Web/ViewModels/OrderItemViewModel.cs b/src/Web/ViewModels/OrderItemViewModel.cs index b45e28b5c..6db3962be 100644 --- a/src/Web/ViewModels/OrderItemViewModel.cs +++ b/src/Web/ViewModels/OrderItemViewModel.cs @@ -1,12 +1,11 @@ -namespace Microsoft.eShopWeb.Web.ViewModels +namespace Microsoft.eShopWeb.Web.ViewModels; + +public class OrderItemViewModel { - public class OrderItemViewModel - { - public int ProductId { get; set; } - public string ProductName { get; set; } - public decimal UnitPrice { get; set; } - public decimal Discount => 0; - public int Units { get; set; } - public string PictureUrl { get; set; } - } + public int ProductId { get; set; } + public string ProductName { get; set; } + public decimal UnitPrice { get; set; } + public decimal Discount => 0; + public int Units { get; set; } + public string PictureUrl { get; set; } } diff --git a/src/Web/ViewModels/OrderViewModel.cs b/src/Web/ViewModels/OrderViewModel.cs index 8f8f279d7..362a928c7 100644 --- a/src/Web/ViewModels/OrderViewModel.cs +++ b/src/Web/ViewModels/OrderViewModel.cs @@ -1,18 +1,17 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -using System; +using System; using System.Collections.Generic; +using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.Web.ViewModels +namespace Microsoft.eShopWeb.Web.ViewModels; + +public class OrderViewModel { - public class OrderViewModel - { - private const string DEFAULT_STATUS = "Pending"; + private const string DEFAULT_STATUS = "Pending"; - public int OrderNumber { get; set; } - public DateTimeOffset OrderDate { get; set; } - public decimal Total { get; set; } - public string Status => DEFAULT_STATUS; - public Address ShippingAddress { get; set; } - public List OrderItems { get; set; } = new List(); - } + public int OrderNumber { get; set; } + public DateTimeOffset OrderDate { get; set; } + public decimal Total { get; set; } + public string Status => DEFAULT_STATUS; + public Address ShippingAddress { get; set; } + public List OrderItems { get; set; } = new List(); } diff --git a/src/Web/ViewModels/PaginationInfoViewModel.cs b/src/Web/ViewModels/PaginationInfoViewModel.cs index 142d922da..1d1dd7255 100644 --- a/src/Web/ViewModels/PaginationInfoViewModel.cs +++ b/src/Web/ViewModels/PaginationInfoViewModel.cs @@ -1,12 +1,11 @@ -namespace Microsoft.eShopWeb.Web.ViewModels +namespace Microsoft.eShopWeb.Web.ViewModels; + +public class PaginationInfoViewModel { - public class PaginationInfoViewModel - { - public int TotalItems { get; set; } - public int ItemsPerPage { get; set; } - public int ActualPage { get; set; } - public int TotalPages { get; set; } - public string Previous { get; set; } - public string Next { get; set; } - } + public int TotalItems { get; set; } + public int ItemsPerPage { get; set; } + public int ActualPage { get; set; } + public int TotalPages { get; set; } + public string Previous { get; set; } + public string Next { get; set; } } diff --git a/src/Web/Views/Manage/ManageNavPages.cs b/src/Web/Views/Manage/ManageNavPages.cs index a0b9bc58b..f64885ddf 100644 --- a/src/Web/Views/Manage/ManageNavPages.cs +++ b/src/Web/Views/Manage/ManageNavPages.cs @@ -1,35 +1,34 @@ -using Microsoft.AspNetCore.Mvc.Rendering; +using System; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using System; -namespace Microsoft.eShopWeb.Web.Views.Manage +namespace Microsoft.eShopWeb.Web.Views.Manage; + +public static class ManageNavPages { - public static class ManageNavPages - { - public static string ActivePageKey => "ActivePage"; + public static string ActivePageKey => "ActivePage"; - public static string Index => "Index"; + public static string Index => "Index"; - public static string ChangePassword => "ChangePassword"; + public static string ChangePassword => "ChangePassword"; - public static string ExternalLogins => "ExternalLogins"; + public static string ExternalLogins => "ExternalLogins"; - public static string TwoFactorAuthentication => "TwoFactorAuthentication"; + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; - public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); + public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); - public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); + public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); - public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); + public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); - public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); - - public static string PageNavClass(ViewContext viewContext, string page) - { - var activePage = viewContext.ViewData["ActivePage"] as string; - return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; - } + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); - public static void AddActivePage(this ViewDataDictionary viewData, string activePage) => viewData[ActivePageKey] = activePage; + public static string PageNavClass(ViewContext viewContext, string page) + { + var activePage = viewContext.ViewData["ActivePage"] as string; + return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; } + + public static void AddActivePage(this ViewDataDictionary viewData, string activePage) => viewData[ActivePageKey] = activePage; } diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index d016923da..1fb252bc8 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Microsoft.eShopWeb.Web aspnet-Web2-1FA3F72E-E7E3-4360-9E49-1CCCD7FE85F7 latest diff --git a/tests/FunctionalTests/FunctionalTests.csproj b/tests/FunctionalTests/FunctionalTests.csproj index 7b5767d81..755cf50c7 100644 --- a/tests/FunctionalTests/FunctionalTests.csproj +++ b/tests/FunctionalTests/FunctionalTests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Microsoft.eShopWeb.FunctionalTests false diff --git a/tests/FunctionalTests/PublicApi/ApiTestFixture.cs b/tests/FunctionalTests/PublicApi/ApiTestFixture.cs index 4443a7157..5435d4330 100644 --- a/tests/FunctionalTests/PublicApi/ApiTestFixture.cs +++ b/tests/FunctionalTests/PublicApi/ApiTestFixture.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Hosting; +using System; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; @@ -7,38 +8,37 @@ using Microsoft.eShopWeb.PublicApi; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System; -namespace Microsoft.eShopWeb.FunctionalTests.PublicApi +namespace Microsoft.eShopWeb.FunctionalTests.PublicApi; + +public class ApiTestFixture : WebApplicationFactory { - public class ApiTestFixture : WebApplicationFactory + protected override void ConfigureWebHost(IWebHostBuilder builder) { - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - builder.UseEnvironment("Testing"); + builder.UseEnvironment("Testing"); - builder.ConfigureServices(services => - { - services.AddEntityFrameworkInMemoryDatabase(); + builder.ConfigureServices(services => + { + services.AddEntityFrameworkInMemoryDatabase(); // Create a new service provider. var provider = services - .AddEntityFrameworkInMemoryDatabase() - .BuildServiceProvider(); + .AddEntityFrameworkInMemoryDatabase() + .BuildServiceProvider(); // Add a database context (ApplicationDbContext) using an in-memory // database for testing. services.AddDbContext(options => - { - options.UseInMemoryDatabase("InMemoryDbForTesting"); - options.UseInternalServiceProvider(provider); - }); + { + options.UseInMemoryDatabase("InMemoryDbForTesting"); + options.UseInternalServiceProvider(provider); + }); - services.AddDbContext(options => - { - options.UseInMemoryDatabase("Identity"); - options.UseInternalServiceProvider(provider); - }); + services.AddDbContext(options => + { + options.UseInMemoryDatabase("Identity"); + options.UseInternalServiceProvider(provider); + }); // Build the service provider. var sp = services.BuildServiceProvider(); @@ -46,34 +46,33 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) // Create a scope to obtain a reference to the database // context (ApplicationDbContext). using (var scope = sp.CreateScope()) - { - var scopedServices = scope.ServiceProvider; - var db = scopedServices.GetRequiredService(); - var loggerFactory = scopedServices.GetRequiredService(); + { + var scopedServices = scope.ServiceProvider; + var db = scopedServices.GetRequiredService(); + var loggerFactory = scopedServices.GetRequiredService(); - var logger = scopedServices - .GetRequiredService>(); + var logger = scopedServices + .GetRequiredService>(); // Ensure the database is created. db.Database.EnsureCreated(); - try - { + try + { // Seed the database with test data. CatalogContextSeed.SeedAsync(db, loggerFactory).Wait(); // seed sample user data var userManager = scopedServices.GetRequiredService>(); - var roleManager = scopedServices.GetRequiredService>(); - AppIdentityDbContextSeed.SeedAsync(userManager, roleManager).Wait(); - } - catch (Exception ex) - { - logger.LogError(ex, $"An error occurred seeding the " + - "database with test messages. Error: {ex.Message}"); - } + var roleManager = scopedServices.GetRequiredService>(); + AppIdentityDbContextSeed.SeedAsync(userManager, roleManager).Wait(); } - }); - } + catch (Exception ex) + { + logger.LogError(ex, $"An error occurred seeding the " + + "database with test messages. Error: {ex.Message}"); + } + } + }); } } diff --git a/tests/FunctionalTests/PublicApi/ApiTokenHelper.cs b/tests/FunctionalTests/PublicApi/ApiTokenHelper.cs index 4b6275451..567f34b13 100644 --- a/tests/FunctionalTests/PublicApi/ApiTokenHelper.cs +++ b/tests/FunctionalTests/PublicApi/ApiTokenHelper.cs @@ -1,50 +1,49 @@ -using Microsoft.eShopWeb.ApplicationCore.Constants; -using Microsoft.IdentityModel.Tokens; -using System; +using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; +using Microsoft.eShopWeb.ApplicationCore.Constants; +using Microsoft.IdentityModel.Tokens; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Api +namespace Microsoft.eShopWeb.FunctionalTests.Web.Api; + +public class ApiTokenHelper { - public class ApiTokenHelper + public static string GetAdminUserToken() { - public static string GetAdminUserToken() - { - string userName = "admin@microsoft.com"; - string[] roles = { "Administrators" }; + string userName = "admin@microsoft.com"; + string[] roles = { "Administrators" }; - return CreateToken(userName, roles); - } + return CreateToken(userName, roles); + } - public static string GetNormalUserToken() - { - string userName = "demouser@microsoft.com"; - string[] roles = { }; + public static string GetNormalUserToken() + { + string userName = "demouser@microsoft.com"; + string[] roles = { }; - return CreateToken(userName, roles); - } + return CreateToken(userName, roles); + } - private static string CreateToken(string userName, string[] roles) + private static string CreateToken(string userName, string[] roles) + { + var claims = new List { new Claim(ClaimTypes.Name, userName) }; + + foreach (var role in roles) { - var claims = new List { new Claim(ClaimTypes.Name, userName) }; - - foreach (var role in roles) - { - claims.Add(new Claim(ClaimTypes.Role, role)); - } - - var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); - var tokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(claims.ToArray()), - Expires = DateTime.UtcNow.AddHours(1), - SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) - }; - var tokenHandler = new JwtSecurityTokenHandler(); - var token = tokenHandler.CreateToken(tokenDescriptor); - return tokenHandler.WriteToken(token); + claims.Add(new Claim(ClaimTypes.Role, role)); } + + var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(claims.ToArray()), + Expires = DateTime.UtcNow.AddHours(1), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + var tokenHandler = new JwtSecurityTokenHandler(); + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); } } diff --git a/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs b/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs index 19c0de46b..ead822eba 100644 --- a/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs +++ b/tests/FunctionalTests/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs @@ -1,44 +1,43 @@ -using Microsoft.eShopWeb.ApplicationCore.Constants; -using Microsoft.eShopWeb.FunctionalTests.PublicApi; -using Microsoft.eShopWeb.PublicApi.AuthEndpoints; -using System.Net.Http; +using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Constants; +using Microsoft.eShopWeb.FunctionalTests.PublicApi; +using Microsoft.eShopWeb.PublicApi.AuthEndpoints; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class AuthenticateEndpoint : IClassFixture { - [Collection("Sequential")] - public class AuthenticateEndpoint : IClassFixture - { - JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - public AuthenticateEndpoint(ApiTestFixture factory) - { - Client = factory.CreateClient(); - } + public AuthenticateEndpoint(ApiTestFixture factory) + { + Client = factory.CreateClient(); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - [Theory] - [InlineData("demouser@microsoft.com", AuthorizationConstants.DEFAULT_PASSWORD, true)] - [InlineData("demouser@microsoft.com", "badpassword", false)] - [InlineData("baduser@microsoft.com", "badpassword", false)] - public async Task ReturnsExpectedResultGivenCredentials(string testUsername, string testPassword, bool expectedResult) + [Theory] + [InlineData("demouser@microsoft.com", AuthorizationConstants.DEFAULT_PASSWORD, true)] + [InlineData("demouser@microsoft.com", "badpassword", false)] + [InlineData("baduser@microsoft.com", "badpassword", false)] + public async Task ReturnsExpectedResultGivenCredentials(string testUsername, string testPassword, bool expectedResult) + { + var request = new AuthenticateRequest() { - var request = new AuthenticateRequest() - { - Username = testUsername, - Password = testPassword - }; - var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); - var response = await Client.PostAsync("api/authenticate", jsonContent); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); - var model = stringResponse.FromJson(); + Username = testUsername, + Password = testPassword + }; + var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); + var response = await Client.PostAsync("api/authenticate", jsonContent); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + var model = stringResponse.FromJson(); - Assert.Equal(expectedResult, model.Result); - } + Assert.Equal(expectedResult, model.Result); } } diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs index 702d830b3..514e29e05 100644 --- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs +++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/ApiCatalogControllerList.cs @@ -1,42 +1,41 @@ -using Microsoft.eShopWeb.FunctionalTests.PublicApi; -using Microsoft.eShopWeb.Web.ViewModels; -using System.Linq; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.eShopWeb.FunctionalTests.PublicApi; +using Microsoft.eShopWeb.Web.ViewModels; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class ApiCatalogControllerList : IClassFixture { - [Collection("Sequential")] - public class ApiCatalogControllerList : IClassFixture + public ApiCatalogControllerList(ApiTestFixture factory) { - public ApiCatalogControllerList(ApiTestFixture factory) - { - Client = factory.CreateClient(); - } + Client = factory.CreateClient(); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - [Fact] - public async Task ReturnsFirst10CatalogItems() - { - var response = await Client.GetAsync("/api/catalog-items?pageSize=10"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); - var model = stringResponse.FromJson(); + [Fact] + public async Task ReturnsFirst10CatalogItems() + { + var response = await Client.GetAsync("/api/catalog-items?pageSize=10"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + var model = stringResponse.FromJson(); - Assert.Equal(10, model.CatalogItems.Count()); - } + Assert.Equal(10, model.CatalogItems.Count()); + } - [Fact] - public async Task ReturnsLast2CatalogItemsGivenPageIndex1() - { - var response = await Client.GetAsync("/api/catalog-items?pageSize=10&pageIndex=1"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); - var model = stringResponse.FromJson(); + [Fact] + public async Task ReturnsLast2CatalogItemsGivenPageIndex1() + { + var response = await Client.GetAsync("/api/catalog-items?pageSize=10&pageIndex=1"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + var model = stringResponse.FromJson(); - Assert.Equal(2, model.CatalogItems.Count()); - } + Assert.Equal(2, model.CatalogItems.Count()); } } diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs index aff247347..855ea3663 100644 --- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs +++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/CreateEndpoint.cs @@ -1,75 +1,74 @@ -using Microsoft.eShopWeb.FunctionalTests.PublicApi; -using Microsoft.eShopWeb.FunctionalTests.Web.Api; -using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; -using System.Net; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.eShopWeb.FunctionalTests.PublicApi; +using Microsoft.eShopWeb.FunctionalTests.Web.Api; +using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class CreateEndpoint : IClassFixture { - [Collection("Sequential")] - public class CreateEndpoint : IClassFixture - { - JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - private int _testBrandId = 1; - private int _testTypeId = 2; - private string _testDescription = "test description"; - private string _testName = "test name"; - private decimal _testPrice = 1.23m; + JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + private int _testBrandId = 1; + private int _testTypeId = 2; + private string _testDescription = "test description"; + private string _testName = "test name"; + private decimal _testPrice = 1.23m; - public CreateEndpoint(ApiTestFixture factory) - { - Client = factory.CreateClient(); - } + public CreateEndpoint(ApiTestFixture factory) + { + Client = factory.CreateClient(); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - [Fact] - public async Task ReturnsNotAuthorizedGivenNormalUserToken() - { - var jsonContent = GetValidNewItemJson(); - var token = ApiTokenHelper.GetNormalUserToken(); - Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); - var response = await Client.PostAsync("api/catalog-items", jsonContent); + [Fact] + public async Task ReturnsNotAuthorizedGivenNormalUserToken() + { + var jsonContent = GetValidNewItemJson(); + var token = ApiTokenHelper.GetNormalUserToken(); + Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + var response = await Client.PostAsync("api/catalog-items", jsonContent); - Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); - } + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } - [Fact] - public async Task ReturnsSuccessGivenValidNewItemAndAdminUserToken() - { - var jsonContent = GetValidNewItemJson(); - var adminToken = ApiTokenHelper.GetAdminUserToken(); - Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken); - var response = await Client.PostAsync("api/catalog-items", jsonContent); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); - var model = stringResponse.FromJson(); + [Fact] + public async Task ReturnsSuccessGivenValidNewItemAndAdminUserToken() + { + var jsonContent = GetValidNewItemJson(); + var adminToken = ApiTokenHelper.GetAdminUserToken(); + Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken); + var response = await Client.PostAsync("api/catalog-items", jsonContent); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + var model = stringResponse.FromJson(); - Assert.Equal(_testBrandId, model.CatalogItem.CatalogBrandId); - Assert.Equal(_testTypeId, model.CatalogItem.CatalogTypeId); - Assert.Equal(_testDescription, model.CatalogItem.Description); - Assert.Equal(_testName, model.CatalogItem.Name); - Assert.Equal(_testPrice, model.CatalogItem.Price); - } + Assert.Equal(_testBrandId, model.CatalogItem.CatalogBrandId); + Assert.Equal(_testTypeId, model.CatalogItem.CatalogTypeId); + Assert.Equal(_testDescription, model.CatalogItem.Description); + Assert.Equal(_testName, model.CatalogItem.Name); + Assert.Equal(_testPrice, model.CatalogItem.Price); + } - private StringContent GetValidNewItemJson() + private StringContent GetValidNewItemJson() + { + var request = new CreateCatalogItemRequest() { - var request = new CreateCatalogItemRequest() - { - CatalogBrandId = _testBrandId, - CatalogTypeId = _testTypeId, - Description = _testDescription, - Name = _testName, - Price = _testPrice - }; - var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); + CatalogBrandId = _testBrandId, + CatalogTypeId = _testTypeId, + Description = _testDescription, + Name = _testName, + Price = _testPrice + }; + var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); - return jsonContent; - } + return jsonContent; } } diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs index 8c23a86ba..dde7e280c 100644 --- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs +++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/DeleteEndpoint.cs @@ -1,48 +1,47 @@ -using Microsoft.eShopWeb.FunctionalTests.PublicApi; -using Microsoft.eShopWeb.FunctionalTests.Web.Api; -using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; -using System.Net; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.eShopWeb.FunctionalTests.PublicApi; +using Microsoft.eShopWeb.FunctionalTests.Web.Api; +using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class DeleteEndpoint : IClassFixture { - [Collection("Sequential")] - public class DeleteEndpoint : IClassFixture + JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + + public DeleteEndpoint(ApiTestFixture factory) { - JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - - public DeleteEndpoint(ApiTestFixture factory) - { - Client = factory.CreateClient(); - } - - public HttpClient Client { get; } - - [Fact] - public async Task ReturnsSuccessGivenValidIdAndAdminUserToken() - { - var adminToken = ApiTokenHelper.GetAdminUserToken(); - Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken); - var response = await Client.DeleteAsync("api/catalog-items/12"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); - var model = stringResponse.FromJson(); - - Assert.Equal("Deleted", model.Status); - } - - [Fact] - public async Task ReturnsNotFoundGivenInvalidIdAndAdminUserToken() - { - var adminToken = ApiTokenHelper.GetAdminUserToken(); - Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken); - var response = await Client.DeleteAsync("api/catalog-items/0"); - - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Client = factory.CreateClient(); + } + + public HttpClient Client { get; } + + [Fact] + public async Task ReturnsSuccessGivenValidIdAndAdminUserToken() + { + var adminToken = ApiTokenHelper.GetAdminUserToken(); + Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken); + var response = await Client.DeleteAsync("api/catalog-items/12"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + var model = stringResponse.FromJson(); + + Assert.Equal("Deleted", model.Status); + } + + [Fact] + public async Task ReturnsNotFoundGivenInvalidIdAndAdminUserToken() + { + var adminToken = ApiTokenHelper.GetAdminUserToken(); + Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", adminToken); + var response = await Client.DeleteAsync("api/catalog-items/0"); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } } diff --git a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs index cd373d343..c8a3b4070 100644 --- a/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs +++ b/tests/FunctionalTests/PublicApi/CatalogItemEndpoints/GetByIdEndpoint.cs @@ -1,43 +1,42 @@ -using Microsoft.eShopWeb.FunctionalTests.PublicApi; -using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; -using System.Net; +using System.Net; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.eShopWeb.FunctionalTests.PublicApi; +using Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class GetByIdEndpoint : IClassFixture { - [Collection("Sequential")] - public class GetByIdEndpoint : IClassFixture + JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + + public GetByIdEndpoint(ApiTestFixture factory) { - JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - - public GetByIdEndpoint(ApiTestFixture factory) - { - Client = factory.CreateClient(); - } - - public HttpClient Client { get; } - - [Fact] - public async Task ReturnsItemGivenValidId() - { - var response = await Client.GetAsync("api/catalog-items/5"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); - var model = stringResponse.FromJson(); - - Assert.Equal(5, model.CatalogItem.Id); - Assert.Equal("Roslyn Red Sheet", model.CatalogItem.Name); - } - - [Fact] - public async Task ReturnsNotFoundGivenInvalidId() - { - var response = await Client.GetAsync("api/catalog-items/0"); - - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Client = factory.CreateClient(); + } + + public HttpClient Client { get; } + + [Fact] + public async Task ReturnsItemGivenValidId() + { + var response = await Client.GetAsync("api/catalog-items/5"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); + var model = stringResponse.FromJson(); + + Assert.Equal(5, model.CatalogItem.Id); + Assert.Equal("Roslyn Red Sheet", model.CatalogItem.Name); + } + + [Fact] + public async Task ReturnsNotFoundGivenInvalidId() + { + var response = await Client.GetAsync("api/catalog-items/0"); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } } diff --git a/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs b/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs index 009a6653d..1625c7fd7 100644 --- a/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs +++ b/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs @@ -1,89 +1,88 @@ -using Microsoft.AspNetCore.Mvc.Testing; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Testing; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class AccountControllerSignIn : IClassFixture { - [Collection("Sequential")] - public class AccountControllerSignIn : IClassFixture + public AccountControllerSignIn(WebTestFixture factory) { - public AccountControllerSignIn(WebTestFixture factory) + Client = factory.CreateClient(new WebApplicationFactoryClientOptions { - Client = factory.CreateClient(new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false - }); - } + AllowAutoRedirect = false + }); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - [Fact] - public async Task ReturnsSignInScreenOnGet() - { - var response = await Client.GetAsync("/identity/account/login"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); + [Fact] + public async Task ReturnsSignInScreenOnGet() + { + var response = await Client.GetAsync("/identity/account/login"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); - Assert.Contains("demouser@microsoft.com", stringResponse); - } + Assert.Contains("demouser@microsoft.com", stringResponse); + } - [Fact] - public void RegexMatchesValidRequestVerificationToken() - { - // TODO: Move to a unit test - // TODO: Move regex to a constant in test project - var input = @""; - string regexpression = @"name=""__RequestVerificationToken"" type=""hidden"" value=""([-A-Za-z0-9+=/\\_]+?)"""; - var regex = new Regex(regexpression); - var match = regex.Match(input); - var group = match.Groups.Values.LastOrDefault(); - Assert.NotNull(group); - Assert.True(group.Value.Length > 50); - } + [Fact] + public void RegexMatchesValidRequestVerificationToken() + { + // TODO: Move to a unit test + // TODO: Move regex to a constant in test project + var input = @""; + string regexpression = @"name=""__RequestVerificationToken"" type=""hidden"" value=""([-A-Za-z0-9+=/\\_]+?)"""; + var regex = new Regex(regexpression); + var match = regex.Match(input); + var group = match.Groups.Values.LastOrDefault(); + Assert.NotNull(group); + Assert.True(group.Value.Length > 50); + } - [Fact] - public async Task ReturnsFormWithRequestVerificationToken() - { - var response = await Client.GetAsync("/identity/account/login"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); + [Fact] + public async Task ReturnsFormWithRequestVerificationToken() + { + var response = await Client.GetAsync("/identity/account/login"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); - string token = GetRequestVerificationToken(stringResponse); - Assert.True(token.Length > 50); - } + string token = GetRequestVerificationToken(stringResponse); + Assert.True(token.Length > 50); + } - private string GetRequestVerificationToken(string input) - { - string regexpression = @"name=""__RequestVerificationToken"" type=""hidden"" value=""([-A-Za-z0-9+=/\\_]+?)"""; - var regex = new Regex(regexpression); - var match = regex.Match(input); - return match.Groups.Values.LastOrDefault().Value; - } + private string GetRequestVerificationToken(string input) + { + string regexpression = @"name=""__RequestVerificationToken"" type=""hidden"" value=""([-A-Za-z0-9+=/\\_]+?)"""; + var regex = new Regex(regexpression); + var match = regex.Match(input); + return match.Groups.Values.LastOrDefault().Value; + } - [Fact] - public async Task ReturnsSuccessfulSignInOnPostWithValidCredentials() - { - var getResponse = await Client.GetAsync("/identity/account/login"); - getResponse.EnsureSuccessStatusCode(); - var stringResponse1 = await getResponse.Content.ReadAsStringAsync(); - string token = GetRequestVerificationToken(stringResponse1); + [Fact] + public async Task ReturnsSuccessfulSignInOnPostWithValidCredentials() + { + var getResponse = await Client.GetAsync("/identity/account/login"); + getResponse.EnsureSuccessStatusCode(); + var stringResponse1 = await getResponse.Content.ReadAsStringAsync(); + string token = GetRequestVerificationToken(stringResponse1); - var keyValues = new List>(); - keyValues.Add(new KeyValuePair("Email", "demouser@microsoft.com")); - keyValues.Add(new KeyValuePair("Password", "Pass@word1")); + var keyValues = new List>(); + keyValues.Add(new KeyValuePair("Email", "demouser@microsoft.com")); + keyValues.Add(new KeyValuePair("Password", "Pass@word1")); - keyValues.Add(new KeyValuePair("__RequestVerificationToken", token)); - var formContent = new FormUrlEncodedContent(keyValues); + keyValues.Add(new KeyValuePair("__RequestVerificationToken", token)); + var formContent = new FormUrlEncodedContent(keyValues); - var postResponse = await Client.PostAsync("/identity/account/login", formContent); - Assert.Equal(HttpStatusCode.Redirect, postResponse.StatusCode); - Assert.Equal(new System.Uri("/", UriKind.Relative), postResponse.Headers.Location); - } + var postResponse = await Client.PostAsync("/identity/account/login", formContent); + Assert.Equal(HttpStatusCode.Redirect, postResponse.StatusCode); + Assert.Equal(new System.Uri("/", UriKind.Relative), postResponse.Headers.Location); } } diff --git a/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs b/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs index 5ca9958e3..c2f26b456 100644 --- a/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs +++ b/tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs @@ -2,28 +2,27 @@ using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class CatalogControllerIndex : IClassFixture { - [Collection("Sequential")] - public class CatalogControllerIndex : IClassFixture + public CatalogControllerIndex(WebTestFixture factory) { - public CatalogControllerIndex(WebTestFixture factory) - { - Client = factory.CreateClient(); - } + Client = factory.CreateClient(); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - [Fact] - public async Task ReturnsHomePageWithProductListing() - { - // Arrange & Act - var response = await Client.GetAsync("/"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); + [Fact] + public async Task ReturnsHomePageWithProductListing() + { + // Arrange & Act + var response = await Client.GetAsync("/"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); - // Assert - Assert.Contains(".NET Bot Black Sweatshirt", stringResponse); - } + // Assert + Assert.Contains(".NET Bot Black Sweatshirt", stringResponse); } } diff --git a/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs b/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs index 3a4d7e4e2..e739f484b 100644 --- a/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs +++ b/tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs @@ -1,32 +1,31 @@ -using Microsoft.AspNetCore.Mvc.Testing; -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Testing; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers +namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers; + +[Collection("Sequential")] +public class OrderIndexOnGet : IClassFixture { - [Collection("Sequential")] - public class OrderIndexOnGet : IClassFixture + public OrderIndexOnGet(WebTestFixture factory) { - public OrderIndexOnGet(WebTestFixture factory) + Client = factory.CreateClient(new WebApplicationFactoryClientOptions { - Client = factory.CreateClient(new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false - }); - } + AllowAutoRedirect = false + }); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - [Fact] - public async Task ReturnsRedirectGivenAnonymousUser() - { - var response = await Client.GetAsync("/order/my-orders"); - var redirectLocation = response.Headers.Location.OriginalString; + [Fact] + public async Task ReturnsRedirectGivenAnonymousUser() + { + var response = await Client.GetAsync("/order/my-orders"); + var redirectLocation = response.Headers.Location.OriginalString; - Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); - Assert.Contains("/Account/Login", redirectLocation); - } + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Contains("/Account/Login", redirectLocation); } } diff --git a/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs b/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs index 4afd7694f..8014c04b1 100644 --- a/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs +++ b/tests/FunctionalTests/Web/Pages/BasketPageCheckout.cs @@ -1,70 +1,69 @@ -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.eShopWeb.FunctionalTests.Web; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.eShopWeb.FunctionalTests.Web; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.WebRazorPages +namespace Microsoft.eShopWeb.FunctionalTests.WebRazorPages; + +[Collection("Sequential")] +public class BasketPageCheckout : IClassFixture { - [Collection("Sequential")] - public class BasketPageCheckout : IClassFixture + public BasketPageCheckout(WebTestFixture factory) { - public BasketPageCheckout(WebTestFixture factory) + Client = factory.CreateClient(new WebApplicationFactoryClientOptions { - Client = factory.CreateClient(new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = true - }); - } + AllowAutoRedirect = true + }); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - private string GetRequestVerificationToken(string input) - { - string regexpression = @"name=""__RequestVerificationToken"" type=""hidden"" value=""([-A-Za-z0-9+=/\\_]+?)"""; - var regex = new Regex(regexpression); - var match = regex.Match(input); - return match.Groups.Values.LastOrDefault().Value; - } + private string GetRequestVerificationToken(string input) + { + string regexpression = @"name=""__RequestVerificationToken"" type=""hidden"" value=""([-A-Za-z0-9+=/\\_]+?)"""; + var regex = new Regex(regexpression); + var match = regex.Match(input); + return match.Groups.Values.LastOrDefault().Value; + } - [Fact] - public async Task RedirectsToLoginIfNotAuthenticated() - { - // Arrange & Act + [Fact] + public async Task RedirectsToLoginIfNotAuthenticated() + { + // Arrange & Act - // Load Home Page - var response = await Client.GetAsync("/"); - response.EnsureSuccessStatusCode(); - var stringResponse1 = await response.Content.ReadAsStringAsync(); + // Load Home Page + var response = await Client.GetAsync("/"); + response.EnsureSuccessStatusCode(); + var stringResponse1 = await response.Content.ReadAsStringAsync(); - string token = GetRequestVerificationToken(stringResponse1); + string token = GetRequestVerificationToken(stringResponse1); - // Add Item to Cart - var keyValues = new List>(); - keyValues.Add(new KeyValuePair("id", "2")); - keyValues.Add(new KeyValuePair("name", "shirt")); + // Add Item to Cart + var keyValues = new List>(); + keyValues.Add(new KeyValuePair("id", "2")); + keyValues.Add(new KeyValuePair("name", "shirt")); - keyValues.Add(new KeyValuePair("price", "19.49")); - keyValues.Add(new KeyValuePair("__RequestVerificationToken", token)); + keyValues.Add(new KeyValuePair("price", "19.49")); + keyValues.Add(new KeyValuePair("__RequestVerificationToken", token)); - var formContent = new FormUrlEncodedContent(keyValues); + var formContent = new FormUrlEncodedContent(keyValues); - var postResponse = await Client.PostAsync("/basket/index", formContent); - postResponse.EnsureSuccessStatusCode(); - var stringResponse = await postResponse.Content.ReadAsStringAsync(); + var postResponse = await Client.PostAsync("/basket/index", formContent); + postResponse.EnsureSuccessStatusCode(); + var stringResponse = await postResponse.Content.ReadAsStringAsync(); - // Assert - Assert.Contains(".NET Black & White Mug", stringResponse); + // Assert + Assert.Contains(".NET Black & White Mug", stringResponse); - keyValues.Clear(); - keyValues.Add(new KeyValuePair("__RequestVerificationToken", token)); + keyValues.Clear(); + keyValues.Add(new KeyValuePair("__RequestVerificationToken", token)); - formContent = new FormUrlEncodedContent(keyValues); - var postResponse2 = await Client.PostAsync("/Basket/Checkout", formContent); - Assert.Contains("/Identity/Account/Login", postResponse2.RequestMessage.RequestUri.ToString()); - } + formContent = new FormUrlEncodedContent(keyValues); + var postResponse2 = await Client.PostAsync("/Basket/Checkout", formContent); + Assert.Contains("/Identity/Account/Login", postResponse2.RequestMessage.RequestUri.ToString()); } } diff --git a/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs b/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs index 650efcbea..873d3eaa5 100644 --- a/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs +++ b/tests/FunctionalTests/Web/Pages/HomePageOnGet.cs @@ -1,30 +1,29 @@ -using Microsoft.eShopWeb.FunctionalTests.Web; -using System.Net.Http; +using System.Net.Http; using System.Threading.Tasks; +using Microsoft.eShopWeb.FunctionalTests.Web; using Xunit; -namespace Microsoft.eShopWeb.FunctionalTests.WebRazorPages +namespace Microsoft.eShopWeb.FunctionalTests.WebRazorPages; + +[Collection("Sequential")] +public class HomePageOnGet : IClassFixture { - [Collection("Sequential")] - public class HomePageOnGet : IClassFixture + public HomePageOnGet(WebTestFixture factory) { - public HomePageOnGet(WebTestFixture factory) - { - Client = factory.CreateClient(); - } + Client = factory.CreateClient(); + } - public HttpClient Client { get; } + public HttpClient Client { get; } - [Fact] - public async Task ReturnsHomePageWithProductListing() - { - // Arrange & Act - var response = await Client.GetAsync("/"); - response.EnsureSuccessStatusCode(); - var stringResponse = await response.Content.ReadAsStringAsync(); + [Fact] + public async Task ReturnsHomePageWithProductListing() + { + // Arrange & Act + var response = await Client.GetAsync("/"); + response.EnsureSuccessStatusCode(); + var stringResponse = await response.Content.ReadAsStringAsync(); - // Assert - Assert.Contains(".NET Bot Black Sweatshirt", stringResponse); - } + // Assert + Assert.Contains(".NET Bot Black Sweatshirt", stringResponse); } } diff --git a/tests/FunctionalTests/Web/WebTestFixture.cs b/tests/FunctionalTests/Web/WebTestFixture.cs index f1c213e42..e7194f751 100644 --- a/tests/FunctionalTests/Web/WebTestFixture.cs +++ b/tests/FunctionalTests/Web/WebTestFixture.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Hosting; +using System; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; @@ -7,38 +8,37 @@ using Microsoft.eShopWeb.Web; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System; -namespace Microsoft.eShopWeb.FunctionalTests.Web +namespace Microsoft.eShopWeb.FunctionalTests.Web; + +public class WebTestFixture : WebApplicationFactory { - public class WebTestFixture : WebApplicationFactory + protected override void ConfigureWebHost(IWebHostBuilder builder) { - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - builder.UseEnvironment("Testing"); + builder.UseEnvironment("Testing"); - builder.ConfigureServices(services => - { - services.AddEntityFrameworkInMemoryDatabase(); + builder.ConfigureServices(services => + { + services.AddEntityFrameworkInMemoryDatabase(); // Create a new service provider. var provider = services - .AddEntityFrameworkInMemoryDatabase() - .BuildServiceProvider(); + .AddEntityFrameworkInMemoryDatabase() + .BuildServiceProvider(); // Add a database context (ApplicationDbContext) using an in-memory // database for testing. services.AddDbContext(options => - { - options.UseInMemoryDatabase("InMemoryDbForTesting"); - options.UseInternalServiceProvider(provider); - }); + { + options.UseInMemoryDatabase("InMemoryDbForTesting"); + options.UseInternalServiceProvider(provider); + }); - services.AddDbContext(options => - { - options.UseInMemoryDatabase("Identity"); - options.UseInternalServiceProvider(provider); - }); + services.AddDbContext(options => + { + options.UseInMemoryDatabase("Identity"); + options.UseInternalServiceProvider(provider); + }); // Build the service provider. var sp = services.BuildServiceProvider(); @@ -46,34 +46,33 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) // Create a scope to obtain a reference to the database // context (ApplicationDbContext). using (var scope = sp.CreateScope()) - { - var scopedServices = scope.ServiceProvider; - var db = scopedServices.GetRequiredService(); - var loggerFactory = scopedServices.GetRequiredService(); + { + var scopedServices = scope.ServiceProvider; + var db = scopedServices.GetRequiredService(); + var loggerFactory = scopedServices.GetRequiredService(); - var logger = scopedServices - .GetRequiredService>(); + var logger = scopedServices + .GetRequiredService>(); // Ensure the database is created. db.Database.EnsureCreated(); - try - { + try + { // Seed the database with test data. CatalogContextSeed.SeedAsync(db, loggerFactory).Wait(); // seed sample user data var userManager = scopedServices.GetRequiredService>(); - var roleManager = scopedServices.GetRequiredService>(); - AppIdentityDbContextSeed.SeedAsync(userManager, roleManager).Wait(); - } - catch (Exception ex) - { - logger.LogError(ex, $"An error occurred seeding the " + - "database with test messages. Error: {ex.Message}"); - } + var roleManager = scopedServices.GetRequiredService>(); + AppIdentityDbContextSeed.SeedAsync(userManager, roleManager).Wait(); } - }); - } + catch (Exception ex) + { + logger.LogError(ex, $"An error occurred seeding the " + + "database with test messages. Error: {ex.Message}"); + } + } + }); } } diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj index 7abfc1e21..9f28dc96e 100644 --- a/tests/IntegrationTests/IntegrationTests.csproj +++ b/tests/IntegrationTests/IntegrationTests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Microsoft.eShopWeb.IntegrationTests false diff --git a/tests/IntegrationTests/Repositories/BasketRepositoryTests/SetQuantities.cs b/tests/IntegrationTests/Repositories/BasketRepositoryTests/SetQuantities.cs index b01da70fe..95ba0e3e0 100644 --- a/tests/IntegrationTests/Repositories/BasketRepositoryTests/SetQuantities.cs +++ b/tests/IntegrationTests/Repositories/BasketRepositoryTests/SetQuantities.cs @@ -1,41 +1,40 @@ -using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Services; using Microsoft.eShopWeb.Infrastructure.Data; using Microsoft.eShopWeb.UnitTests.Builders; -using System.Collections.Generic; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.IntegrationTests.Repositories.BasketRepositoryTests +namespace Microsoft.eShopWeb.IntegrationTests.Repositories.BasketRepositoryTests; + +public class SetQuantities { - public class SetQuantities - { - private readonly CatalogContext _catalogContext; - private readonly EfRepository _basketRepository; - private readonly BasketBuilder BasketBuilder = new BasketBuilder(); + private readonly CatalogContext _catalogContext; + private readonly EfRepository _basketRepository; + private readonly BasketBuilder BasketBuilder = new BasketBuilder(); - public SetQuantities() - { - var dbOptions = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "TestCatalog") - .Options; - _catalogContext = new CatalogContext(dbOptions); - _basketRepository = new EfRepository(_catalogContext); - } + public SetQuantities() + { + var dbOptions = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "TestCatalog") + .Options; + _catalogContext = new CatalogContext(dbOptions); + _basketRepository = new EfRepository(_catalogContext); + } - [Fact] - public async Task RemoveEmptyQuantities() - { - var basket = BasketBuilder.WithOneBasketItem(); - var basketService = new BasketService(_basketRepository, null); - await _basketRepository.AddAsync(basket); - _catalogContext.SaveChanges(); + [Fact] + public async Task RemoveEmptyQuantities() + { + var basket = BasketBuilder.WithOneBasketItem(); + var basketService = new BasketService(_basketRepository, null); + await _basketRepository.AddAsync(basket); + _catalogContext.SaveChanges(); - await basketService.SetQuantities(BasketBuilder.BasketId, new Dictionary() { { BasketBuilder.BasketId.ToString(), 0 } }); + await basketService.SetQuantities(BasketBuilder.BasketId, new Dictionary() { { BasketBuilder.BasketId.ToString(), 0 } }); - Assert.Equal(0, basket.Items.Count); - } + Assert.Equal(0, basket.Items.Count); } } diff --git a/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetById.cs b/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetById.cs index beb168895..cd1309c04 100644 --- a/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetById.cs +++ b/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetById.cs @@ -1,46 +1,45 @@ -using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.Infrastructure.Data; using Microsoft.eShopWeb.UnitTests.Builders; -using System.Linq; -using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; -namespace Microsoft.eShopWeb.IntegrationTests.Repositories.OrderRepositoryTests +namespace Microsoft.eShopWeb.IntegrationTests.Repositories.OrderRepositoryTests; + +public class GetById { - public class GetById + private readonly CatalogContext _catalogContext; + private readonly EfRepository _orderRepository; + private OrderBuilder OrderBuilder { get; } = new OrderBuilder(); + private readonly ITestOutputHelper _output; + public GetById(ITestOutputHelper output) { - private readonly CatalogContext _catalogContext; - private readonly EfRepository _orderRepository; - private OrderBuilder OrderBuilder { get; } = new OrderBuilder(); - private readonly ITestOutputHelper _output; - public GetById(ITestOutputHelper output) - { - _output = output; - var dbOptions = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "TestCatalog") - .Options; - _catalogContext = new CatalogContext(dbOptions); - _orderRepository = new EfRepository(_catalogContext); - } + _output = output; + var dbOptions = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "TestCatalog") + .Options; + _catalogContext = new CatalogContext(dbOptions); + _orderRepository = new EfRepository(_catalogContext); + } - [Fact] - public async Task GetsExistingOrder() - { - var existingOrder = OrderBuilder.WithDefaultValues(); - _catalogContext.Orders.Add(existingOrder); - _catalogContext.SaveChanges(); - int orderId = existingOrder.Id; - _output.WriteLine($"OrderId: {orderId}"); + [Fact] + public async Task GetsExistingOrder() + { + var existingOrder = OrderBuilder.WithDefaultValues(); + _catalogContext.Orders.Add(existingOrder); + _catalogContext.SaveChanges(); + int orderId = existingOrder.Id; + _output.WriteLine($"OrderId: {orderId}"); - var orderFromRepo = await _orderRepository.GetByIdAsync(orderId); - Assert.Equal(OrderBuilder.TestBuyerId, orderFromRepo.BuyerId); + var orderFromRepo = await _orderRepository.GetByIdAsync(orderId); + Assert.Equal(OrderBuilder.TestBuyerId, orderFromRepo.BuyerId); - // Note: Using InMemoryDatabase OrderItems is available. Will be null if using SQL DB. - // Use the OrderWithItemsByIdSpec instead of just GetById to get the full aggregate - var firstItem = orderFromRepo.OrderItems.FirstOrDefault(); - Assert.Equal(OrderBuilder.TestUnits, firstItem.Units); - } + // Note: Using InMemoryDatabase OrderItems is available. Will be null if using SQL DB. + // Use the OrderWithItemsByIdSpec instead of just GetById to get the full aggregate + var firstItem = orderFromRepo.OrderItems.FirstOrDefault(); + Assert.Equal(OrderBuilder.TestUnits, firstItem.Units); } } diff --git a/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetByIdWithItemsAsync.cs b/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetByIdWithItemsAsync.cs index 14ae1fbef..a7d3e56de 100644 --- a/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetByIdWithItemsAsync.cs +++ b/tests/IntegrationTests/Repositories/OrderRepositoryTests/GetByIdWithItemsAsync.cs @@ -1,63 +1,62 @@ -using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.Infrastructure.Data; using Microsoft.eShopWeb.UnitTests.Builders; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.IntegrationTests.Repositories.OrderRepositoryTests +namespace Microsoft.eShopWeb.IntegrationTests.Repositories.OrderRepositoryTests; + +public class GetByIdWithItemsAsync { - public class GetByIdWithItemsAsync + private readonly CatalogContext _catalogContext; + private readonly EfRepository _orderRepository; + private OrderBuilder OrderBuilder { get; } = new OrderBuilder(); + + public GetByIdWithItemsAsync() + { + var dbOptions = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "TestCatalog") + .Options; + _catalogContext = new CatalogContext(dbOptions); + _orderRepository = new EfRepository(_catalogContext); + } + + [Fact] + public async Task GetOrderAndItemsByOrderIdWhenMultipleOrdersPresent() { - private readonly CatalogContext _catalogContext; - private readonly EfRepository _orderRepository; - private OrderBuilder OrderBuilder { get; } = new OrderBuilder(); - - public GetByIdWithItemsAsync() - { - var dbOptions = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "TestCatalog") - .Options; - _catalogContext = new CatalogContext(dbOptions); - _orderRepository = new EfRepository(_catalogContext); - } - - [Fact] - public async Task GetOrderAndItemsByOrderIdWhenMultipleOrdersPresent() - { - //Arrange - var itemOneUnitPrice = 5.50m; - var itemOneUnits = 2; - var itemTwoUnitPrice = 7.50m; - var itemTwoUnits = 5; - - var firstOrder = OrderBuilder.WithDefaultValues(); - _catalogContext.Orders.Add(firstOrder); - int firstOrderId = firstOrder.Id; - - var secondOrderItems = new List(); - secondOrderItems.Add(new OrderItem(OrderBuilder.TestCatalogItemOrdered, itemOneUnitPrice, itemOneUnits)); - secondOrderItems.Add(new OrderItem(OrderBuilder.TestCatalogItemOrdered, itemTwoUnitPrice, itemTwoUnits)); - var secondOrder = OrderBuilder.WithItems(secondOrderItems); - _catalogContext.Orders.Add(secondOrder); - int secondOrderId = secondOrder.Id; - - _catalogContext.SaveChanges(); - - //Act - var spec = new OrderWithItemsByIdSpec(secondOrderId); - var orderFromRepo = await _orderRepository.GetBySpecAsync(spec); - - //Assert - Assert.Equal(secondOrderId, orderFromRepo.Id); - Assert.Equal(secondOrder.OrderItems.Count, orderFromRepo.OrderItems.Count); - Assert.Equal(1, orderFromRepo.OrderItems.Count(x => x.UnitPrice == itemOneUnitPrice)); - Assert.Equal(1, orderFromRepo.OrderItems.Count(x => x.UnitPrice == itemTwoUnitPrice)); - Assert.Equal(itemOneUnits, orderFromRepo.OrderItems.SingleOrDefault(x => x.UnitPrice == itemOneUnitPrice).Units); - Assert.Equal(itemTwoUnits, orderFromRepo.OrderItems.SingleOrDefault(x => x.UnitPrice == itemTwoUnitPrice).Units); - } + //Arrange + var itemOneUnitPrice = 5.50m; + var itemOneUnits = 2; + var itemTwoUnitPrice = 7.50m; + var itemTwoUnits = 5; + + var firstOrder = OrderBuilder.WithDefaultValues(); + _catalogContext.Orders.Add(firstOrder); + int firstOrderId = firstOrder.Id; + + var secondOrderItems = new List(); + secondOrderItems.Add(new OrderItem(OrderBuilder.TestCatalogItemOrdered, itemOneUnitPrice, itemOneUnits)); + secondOrderItems.Add(new OrderItem(OrderBuilder.TestCatalogItemOrdered, itemTwoUnitPrice, itemTwoUnits)); + var secondOrder = OrderBuilder.WithItems(secondOrderItems); + _catalogContext.Orders.Add(secondOrder); + int secondOrderId = secondOrder.Id; + + _catalogContext.SaveChanges(); + + //Act + var spec = new OrderWithItemsByIdSpec(secondOrderId); + var orderFromRepo = await _orderRepository.GetBySpecAsync(spec); + + //Assert + Assert.Equal(secondOrderId, orderFromRepo.Id); + Assert.Equal(secondOrder.OrderItems.Count, orderFromRepo.OrderItems.Count); + Assert.Equal(1, orderFromRepo.OrderItems.Count(x => x.UnitPrice == itemOneUnitPrice)); + Assert.Equal(1, orderFromRepo.OrderItems.Count(x => x.UnitPrice == itemTwoUnitPrice)); + Assert.Equal(itemOneUnits, orderFromRepo.OrderItems.SingleOrDefault(x => x.UnitPrice == itemOneUnitPrice).Units); + Assert.Equal(itemTwoUnits, orderFromRepo.OrderItems.SingleOrDefault(x => x.UnitPrice == itemTwoUnitPrice).Units); } } diff --git a/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketAddItem.cs b/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketAddItem.cs index cf7cc98ac..18a47cdc1 100644 --- a/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketAddItem.cs +++ b/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketAddItem.cs @@ -1,76 +1,75 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; -using System; +using System; using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.BasketTests -{ - public class BasketAddItem - { - private readonly int _testCatalogItemId = 123; - private readonly decimal _testUnitPrice = 1.23m; - private readonly int _testQuantity = 2; - private readonly string _buyerId = "Test buyerId"; - - [Fact] - public void AddsBasketItemIfNotPresent() - { - var basket = new Basket(_buyerId); - basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); - - var firstItem = basket.Items.Single(); - Assert.Equal(_testCatalogItemId, firstItem.CatalogItemId); - Assert.Equal(_testUnitPrice, firstItem.UnitPrice); - Assert.Equal(_testQuantity, firstItem.Quantity); - } - - [Fact] - public void IncrementsQuantityOfItemIfPresent() - { - var basket = new Basket(_buyerId); - basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); - basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); - - var firstItem = basket.Items.Single(); - Assert.Equal(_testQuantity * 2, firstItem.Quantity); - } - - [Fact] - public void KeepsOriginalUnitPriceIfMoreItemsAdded() - { - var basket = new Basket(_buyerId); - basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); - basket.AddItem(_testCatalogItemId, _testUnitPrice * 2, _testQuantity); - - var firstItem = basket.Items.Single(); - Assert.Equal(_testUnitPrice, firstItem.UnitPrice); - } - - [Fact] - public void DefaultsToQuantityOfOne() - { - var basket = new Basket(_buyerId); - basket.AddItem(_testCatalogItemId, _testUnitPrice); - - var firstItem = basket.Items.Single(); - Assert.Equal(1, firstItem.Quantity); - } +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.BasketTests; + +public class BasketAddItem +{ + private readonly int _testCatalogItemId = 123; + private readonly decimal _testUnitPrice = 1.23m; + private readonly int _testQuantity = 2; + private readonly string _buyerId = "Test buyerId"; + + [Fact] + public void AddsBasketItemIfNotPresent() + { + var basket = new Basket(_buyerId); + basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); + + var firstItem = basket.Items.Single(); + Assert.Equal(_testCatalogItemId, firstItem.CatalogItemId); + Assert.Equal(_testUnitPrice, firstItem.UnitPrice); + Assert.Equal(_testQuantity, firstItem.Quantity); + } + + [Fact] + public void IncrementsQuantityOfItemIfPresent() + { + var basket = new Basket(_buyerId); + basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); + basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); + + var firstItem = basket.Items.Single(); + Assert.Equal(_testQuantity * 2, firstItem.Quantity); + } + + [Fact] + public void KeepsOriginalUnitPriceIfMoreItemsAdded() + { + var basket = new Basket(_buyerId); + basket.AddItem(_testCatalogItemId, _testUnitPrice, _testQuantity); + basket.AddItem(_testCatalogItemId, _testUnitPrice * 2, _testQuantity); + + var firstItem = basket.Items.Single(); + Assert.Equal(_testUnitPrice, firstItem.UnitPrice); + } + + [Fact] + public void DefaultsToQuantityOfOne() + { + var basket = new Basket(_buyerId); + basket.AddItem(_testCatalogItemId, _testUnitPrice); + + var firstItem = basket.Items.Single(); + Assert.Equal(1, firstItem.Quantity); + } - [Fact] - public void CantAddItemWithNegativeQuantity() - { - var basket = new Basket(_buyerId); + [Fact] + public void CantAddItemWithNegativeQuantity() + { + var basket = new Basket(_buyerId); - Assert.Throws(() => basket.AddItem(_testCatalogItemId, _testUnitPrice, -1)); - } + Assert.Throws(() => basket.AddItem(_testCatalogItemId, _testUnitPrice, -1)); + } - [Fact] - public void CantModifyQuantityToNegativeNumber() - { - var basket = new Basket(_buyerId); - basket.AddItem(_testCatalogItemId, _testUnitPrice); + [Fact] + public void CantModifyQuantityToNegativeNumber() + { + var basket = new Basket(_buyerId); + basket.AddItem(_testCatalogItemId, _testUnitPrice); - Assert.Throws(() => basket.AddItem(_testCatalogItemId, _testUnitPrice, -2)); - } - } + Assert.Throws(() => basket.AddItem(_testCatalogItemId, _testUnitPrice, -2)); + } } diff --git a/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketRemoveEmptyItems.cs b/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketRemoveEmptyItems.cs index 0ce6320c2..58aebb42f 100644 --- a/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketRemoveEmptyItems.cs +++ b/tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketRemoveEmptyItems.cs @@ -1,22 +1,21 @@ using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.BasketTests +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.BasketTests; + +public class BasketRemoveEmptyItems { - public class BasketRemoveEmptyItems - { - private readonly int _testCatalogItemId = 123; - private readonly decimal _testUnitPrice = 1.23m; - private readonly string _buyerId = "Test buyerId"; + private readonly int _testCatalogItemId = 123; + private readonly decimal _testUnitPrice = 1.23m; + private readonly string _buyerId = "Test buyerId"; - [Fact] - public void RemovesEmptyBasketItems() - { - var basket = new Basket(_buyerId); - basket.AddItem(_testCatalogItemId, _testUnitPrice, 0); - basket.RemoveEmptyItems(); + [Fact] + public void RemovesEmptyBasketItems() + { + var basket = new Basket(_buyerId); + basket.AddItem(_testCatalogItemId, _testUnitPrice, 0); + basket.RemoveEmptyItems(); - Assert.Equal(0, basket.Items.Count); - } + Assert.Equal(0, basket.Items.Count); } } diff --git a/tests/UnitTests/ApplicationCore/Entities/CatalogItemTests/UpdateDetails.cs b/tests/UnitTests/ApplicationCore/Entities/CatalogItemTests/UpdateDetails.cs index 3f767b946..edc6f2bd3 100644 --- a/tests/UnitTests/ApplicationCore/Entities/CatalogItemTests/UpdateDetails.cs +++ b/tests/UnitTests/ApplicationCore/Entities/CatalogItemTests/UpdateDetails.cs @@ -1,56 +1,55 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities; -using System; +using System; +using Microsoft.eShopWeb.ApplicationCore.Entities; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.CatalogItemTests +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.CatalogItemTests; + +public class UpdateDetails { - public class UpdateDetails + private CatalogItem _testItem; + private int _validTypeId = 1; + private int _validBrandId = 2; + private string _validDescription = "test description"; + private string _validName = "test name"; + private decimal _validPrice = 1.23m; + private string _validUri = "/123"; + + public UpdateDetails() + { + _testItem = new CatalogItem(_validTypeId, _validBrandId, _validDescription, _validName, _validPrice, _validUri); + } + + [Fact] + public void ThrowsArgumentExceptionGivenEmptyName() + { + string newValue = ""; + Assert.Throws(() => _testItem.UpdateDetails(newValue, _validDescription, _validPrice)); + } + + [Fact] + public void ThrowsArgumentExceptionGivenEmptyDescription() + { + string newValue = ""; + Assert.Throws(() => _testItem.UpdateDetails(_validName, newValue, _validPrice)); + } + + [Fact] + public void ThrowsArgumentNullExceptionGivenNullName() + { + Assert.Throws(() => _testItem.UpdateDetails(null, _validDescription, _validPrice)); + } + + [Fact] + public void ThrowsArgumentNullExceptionGivenNullDescription() + { + Assert.Throws(() => _testItem.UpdateDetails(_validName, null, _validPrice)); + } + + [Theory] + [InlineData(0)] + [InlineData(-1.23)] + public void ThrowsArgumentExceptionGivenNonPositivePrice(decimal newPrice) { - private CatalogItem _testItem; - private int _validTypeId = 1; - private int _validBrandId = 2; - private string _validDescription = "test description"; - private string _validName = "test name"; - private decimal _validPrice = 1.23m; - private string _validUri = "/123"; - - public UpdateDetails() - { - _testItem = new CatalogItem(_validTypeId, _validBrandId, _validDescription, _validName, _validPrice, _validUri); - } - - [Fact] - public void ThrowsArgumentExceptionGivenEmptyName() - { - string newValue = ""; - Assert.Throws(() => _testItem.UpdateDetails(newValue, _validDescription, _validPrice)); - } - - [Fact] - public void ThrowsArgumentExceptionGivenEmptyDescription() - { - string newValue = ""; - Assert.Throws(() => _testItem.UpdateDetails(_validName, newValue, _validPrice)); - } - - [Fact] - public void ThrowsArgumentNullExceptionGivenNullName() - { - Assert.Throws(() => _testItem.UpdateDetails(null, _validDescription, _validPrice)); - } - - [Fact] - public void ThrowsArgumentNullExceptionGivenNullDescription() - { - Assert.Throws(() => _testItem.UpdateDetails(_validName, null, _validPrice)); - } - - [Theory] - [InlineData(0)] - [InlineData(-1.23)] - public void ThrowsArgumentExceptionGivenNonPositivePrice(decimal newPrice) - { - Assert.Throws(() => _testItem.UpdateDetails(_validName, _validDescription, newPrice)); - } + Assert.Throws(() => _testItem.UpdateDetails(_validName, _validDescription, newPrice)); } } diff --git a/tests/UnitTests/ApplicationCore/Entities/OrderTests/OrderTotal.cs b/tests/UnitTests/ApplicationCore/Entities/OrderTests/OrderTotal.cs index 709c00a0a..d6b3357c8 100644 --- a/tests/UnitTests/ApplicationCore/Entities/OrderTests/OrderTotal.cs +++ b/tests/UnitTests/ApplicationCore/Entities/OrderTests/OrderTotal.cs @@ -1,41 +1,40 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; +using System.Collections.Generic; +using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.UnitTests.Builders; -using System.Collections.Generic; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.OrderTests +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Entities.OrderTests; + +public class OrderTotal { - public class OrderTotal - { - private decimal _testUnitPrice = 42m; + private decimal _testUnitPrice = 42m; - [Fact] - public void IsZeroForNewOrder() - { - var order = new OrderBuilder().WithNoItems(); + [Fact] + public void IsZeroForNewOrder() + { + var order = new OrderBuilder().WithNoItems(); - Assert.Equal(0, order.Total()); - } + Assert.Equal(0, order.Total()); + } - [Fact] - public void IsCorrectGiven1Item() - { - var builder = new OrderBuilder(); - var items = new List + [Fact] + public void IsCorrectGiven1Item() + { + var builder = new OrderBuilder(); + var items = new List { new OrderItem(builder.TestCatalogItemOrdered, _testUnitPrice, 1) }; - var order = new OrderBuilder().WithItems(items); - Assert.Equal(_testUnitPrice, order.Total()); - } + var order = new OrderBuilder().WithItems(items); + Assert.Equal(_testUnitPrice, order.Total()); + } - [Fact] - public void IsCorrectGiven3Items() - { - var builder = new OrderBuilder(); - var order = builder.WithDefaultValues(); + [Fact] + public void IsCorrectGiven3Items() + { + var builder = new OrderBuilder(); + var order = builder.WithDefaultValues(); - Assert.Equal(builder.TestUnitPrice * builder.TestUnits, order.Total()); - } + Assert.Equal(builder.TestUnitPrice * builder.TestUnits, order.Total()); } } diff --git a/tests/UnitTests/ApplicationCore/Extensions/JsonExtensions.cs b/tests/UnitTests/ApplicationCore/Extensions/JsonExtensions.cs index c9842243e..424b0d372 100644 --- a/tests/UnitTests/ApplicationCore/Extensions/JsonExtensions.cs +++ b/tests/UnitTests/ApplicationCore/Extensions/JsonExtensions.cs @@ -1,36 +1,35 @@ using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Extensions +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Extensions; + +public class JsonExtensions { - public class JsonExtensions + [Fact] + public void CorrectlySerializesAndDeserializesObject() { - [Fact] - public void CorrectlySerializesAndDeserializesObject() + var testParent = new TestParent { - var testParent = new TestParent + Id = 7, + Name = "Test name", + Children = new[] { - Id = 7, - Name = "Test name", - Children = new[] - { new TestChild(), new TestChild(), new TestChild() } - }; + }; - var json = testParent.ToJson(); - var result = json.FromJson(); - Assert.Equal(testParent, result); - } + var json = testParent.ToJson(); + var result = json.FromJson(); + Assert.Equal(testParent, result); + } - [ - Theory, - InlineData("{ \"id\": 9, \"name\": \"Another test\" }", 9, "Another test"), - InlineData("{ \"id\": 3124, \"name\": \"Test Value 1\" }", 3124, "Test Value 1"), - ] - public void CorrectlyDeserializesJson(string json, int expectedId, string expectedName) => - Assert.Equal(new TestParent { Id = expectedId, Name = expectedName }, json.FromJson()); + [ + Theory, + InlineData("{ \"id\": 9, \"name\": \"Another test\" }", 9, "Another test"), + InlineData("{ \"id\": 3124, \"name\": \"Test Value 1\" }", 3124, "Test Value 1"), + ] + public void CorrectlyDeserializesJson(string json, int expectedId, string expectedName) => + Assert.Equal(new TestParent { Id = expectedId, Name = expectedName }, json.FromJson()); - } -} \ No newline at end of file +} diff --git a/tests/UnitTests/ApplicationCore/Extensions/TestChild.cs b/tests/UnitTests/ApplicationCore/Extensions/TestChild.cs index bd07bf3aa..2b19d6667 100644 --- a/tests/UnitTests/ApplicationCore/Extensions/TestChild.cs +++ b/tests/UnitTests/ApplicationCore/Extensions/TestChild.cs @@ -2,16 +2,15 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Extensions +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Extensions; + +[DebuggerDisplay("Id={Id}, Date={Date}")] +public class TestChild : IEquatable { - [DebuggerDisplay("Id={Id}, Date={Date}")] - public class TestChild : IEquatable - { - public Guid Id { get; set; } = Guid.NewGuid(); + public Guid Id { get; set; } = Guid.NewGuid(); - public DateTime Date { get; set; } = DateTime.UtcNow; + public DateTime Date { get; set; } = DateTime.UtcNow; - public bool Equals([AllowNull] TestChild other) => - other?.Date == Date && other?.Id == Id; - } + public bool Equals([AllowNull] TestChild other) => + other?.Date == Date && other?.Id == Id; } diff --git a/tests/UnitTests/ApplicationCore/Extensions/TestParent.cs b/tests/UnitTests/ApplicationCore/Extensions/TestParent.cs index eb5541e28..bd20e36b8 100644 --- a/tests/UnitTests/ApplicationCore/Extensions/TestParent.cs +++ b/tests/UnitTests/ApplicationCore/Extensions/TestParent.cs @@ -3,19 +3,18 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Extensions +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Extensions; + +public class TestParent : IEquatable { - public class TestParent : IEquatable - { - public int Id { get; set; } + public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public IEnumerable Children { get; set; } + public IEnumerable Children { get; set; } - public bool Equals([AllowNull] TestParent other) => - other?.Id == Id && other?.Name == Name && - (other?.Children is null && Children is null || - (other?.Children?.Zip(Children)?.All(t => t.First?.Equals(t.Second) ?? false) ?? false)); - } -} \ No newline at end of file + public bool Equals([AllowNull] TestParent other) => + other?.Id == Id && other?.Name == Name && + (other?.Children is null && Children is null || + (other?.Children?.Zip(Children)?.All(t => t.First?.Equals(t.Second) ?? false) ?? false)); +} diff --git a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/AddItemToBasket.cs b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/AddItemToBasket.cs index e46a154e8..7937d082d 100644 --- a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/AddItemToBasket.cs +++ b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/AddItemToBasket.cs @@ -1,44 +1,43 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Services; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Moq; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests; + +public class AddItemToBasket { - public class AddItemToBasket - { - private readonly string _buyerId = "Test buyerId"; - private readonly Mock> _mockBasketRepo = new(); + private readonly string _buyerId = "Test buyerId"; + private readonly Mock> _mockBasketRepo = new(); - [Fact] - public async Task InvokesBasketRepositoryGetBySpecAsyncOnce() - { - var basket = new Basket(_buyerId); - basket.AddItem(1, It.IsAny(), It.IsAny()); - _mockBasketRepo.Setup(x => x.GetBySpecAsync(It.IsAny(), default)).ReturnsAsync(basket); + [Fact] + public async Task InvokesBasketRepositoryGetBySpecAsyncOnce() + { + var basket = new Basket(_buyerId); + basket.AddItem(1, It.IsAny(), It.IsAny()); + _mockBasketRepo.Setup(x => x.GetBySpecAsync(It.IsAny(), default)).ReturnsAsync(basket); - var basketService = new BasketService(_mockBasketRepo.Object, null); + var basketService = new BasketService(_mockBasketRepo.Object, null); - await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m); + await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m); - _mockBasketRepo.Verify(x => x.GetBySpecAsync(It.IsAny(), default), Times.Once); - } + _mockBasketRepo.Verify(x => x.GetBySpecAsync(It.IsAny(), default), Times.Once); + } - [Fact] - public async Task InvokesBasketRepositoryUpdateAsyncOnce() - { - var basket = new Basket(_buyerId); - basket.AddItem(1, It.IsAny(), It.IsAny()); - _mockBasketRepo.Setup(x => x.GetBySpecAsync(It.IsAny(), default)).ReturnsAsync(basket); + [Fact] + public async Task InvokesBasketRepositoryUpdateAsyncOnce() + { + var basket = new Basket(_buyerId); + basket.AddItem(1, It.IsAny(), It.IsAny()); + _mockBasketRepo.Setup(x => x.GetBySpecAsync(It.IsAny(), default)).ReturnsAsync(basket); - var basketService = new BasketService(_mockBasketRepo.Object, null); + var basketService = new BasketService(_mockBasketRepo.Object, null); - await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m); + await basketService.AddItemToBasket(basket.BuyerId, 1, 1.50m); - _mockBasketRepo.Verify(x => x.UpdateAsync(basket, default), Times.Once); - } + _mockBasketRepo.Verify(x => x.UpdateAsync(basket, default), Times.Once); } } diff --git a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/DeleteBasket.cs b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/DeleteBasket.cs index 2787a09ce..01eff0486 100644 --- a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/DeleteBasket.cs +++ b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/DeleteBasket.cs @@ -1,30 +1,29 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Services; using Moq; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests; + +public class DeleteBasket { - public class DeleteBasket - { - private readonly string _buyerId = "Test buyerId"; - private readonly Mock> _mockBasketRepo = new(); + private readonly string _buyerId = "Test buyerId"; + private readonly Mock> _mockBasketRepo = new(); - [Fact] - public async Task ShouldInvokeBasketRepositoryDeleteAsyncOnce() - { - var basket = new Basket(_buyerId); - basket.AddItem(1, It.IsAny(), It.IsAny()); - basket.AddItem(2, It.IsAny(), It.IsAny()); - _mockBasketRepo.Setup(x => x.GetByIdAsync(It.IsAny(),default)) - .ReturnsAsync(basket); - var basketService = new BasketService(_mockBasketRepo.Object, null); + [Fact] + public async Task ShouldInvokeBasketRepositoryDeleteAsyncOnce() + { + var basket = new Basket(_buyerId); + basket.AddItem(1, It.IsAny(), It.IsAny()); + basket.AddItem(2, It.IsAny(), It.IsAny()); + _mockBasketRepo.Setup(x => x.GetByIdAsync(It.IsAny(), default)) + .ReturnsAsync(basket); + var basketService = new BasketService(_mockBasketRepo.Object, null); - await basketService.DeleteBasketAsync(It.IsAny()); + await basketService.DeleteBasketAsync(It.IsAny()); - _mockBasketRepo.Verify(x => x.DeleteAsync(It.IsAny(),default), Times.Once); - } + _mockBasketRepo.Verify(x => x.DeleteAsync(It.IsAny(), default), Times.Once); } } diff --git a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs index df17749b5..57d4d6c97 100644 --- a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs +++ b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/SetQuantities.cs @@ -1,35 +1,34 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; +using System; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Exceptions; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Services; using Moq; -using System; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests; + +public class SetQuantities { - public class SetQuantities - { - private readonly int _invalidId = -1; - private readonly Mock> _mockBasketRepo = new(); + private readonly int _invalidId = -1; + private readonly Mock> _mockBasketRepo = new(); - [Fact] - public async Task ThrowsGivenInvalidBasketId() - { - var basketService = new BasketService(_mockBasketRepo.Object, null); + [Fact] + public async Task ThrowsGivenInvalidBasketId() + { + var basketService = new BasketService(_mockBasketRepo.Object, null); - await Assert.ThrowsAsync(async () => - await basketService.SetQuantities(_invalidId, new System.Collections.Generic.Dictionary())); - } + await Assert.ThrowsAsync(async () => + await basketService.SetQuantities(_invalidId, new System.Collections.Generic.Dictionary())); + } - [Fact] - public async Task ThrowsGivenNullQuantities() - { - var basketService = new BasketService(null, null); + [Fact] + public async Task ThrowsGivenNullQuantities() + { + var basketService = new BasketService(null, null); - await Assert.ThrowsAsync(async () => - await basketService.SetQuantities(123, null)); - } + await Assert.ThrowsAsync(async () => + await basketService.SetQuantities(123, null)); } } diff --git a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs index 0932963ef..390e9eba8 100644 --- a/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs +++ b/tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs @@ -1,97 +1,96 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; +using System; +using System.Threading.Tasks; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Services; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Moq; -using System; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Services.BasketServiceTests; + +public class TransferBasket { - public class TransferBasket - { - private readonly string _nonexistentAnonymousBasketBuyerId = "nonexistent-anonymous-basket-buyer-id"; - private readonly string _existentAnonymousBasketBuyerId = "existent-anonymous-basket-buyer-id"; - private readonly string _nonexistentUserBasketBuyerId = "newuser@microsoft.com"; - private readonly string _existentUserBasketBuyerId = "testuser@microsoft.com"; - private readonly Mock> _mockBasketRepo = new(); + private readonly string _nonexistentAnonymousBasketBuyerId = "nonexistent-anonymous-basket-buyer-id"; + private readonly string _existentAnonymousBasketBuyerId = "existent-anonymous-basket-buyer-id"; + private readonly string _nonexistentUserBasketBuyerId = "newuser@microsoft.com"; + private readonly string _existentUserBasketBuyerId = "testuser@microsoft.com"; + private readonly Mock> _mockBasketRepo = new(); - [Fact] - public async Task ThrowsGivenNullAnonymousId() - { - var basketService = new BasketService(null, null); + [Fact] + public async Task ThrowsGivenNullAnonymousId() + { + var basketService = new BasketService(null, null); - await Assert.ThrowsAsync(async () => await basketService.TransferBasketAsync(null, "steve")); - } + await Assert.ThrowsAsync(async () => await basketService.TransferBasketAsync(null, "steve")); + } - [Fact] - public async Task ThrowsGivenNullUserId() - { - var basketService = new BasketService(null, null); + [Fact] + public async Task ThrowsGivenNullUserId() + { + var basketService = new BasketService(null, null); - await Assert.ThrowsAsync(async () => await basketService.TransferBasketAsync("abcdefg", null)); - } + await Assert.ThrowsAsync(async () => await basketService.TransferBasketAsync("abcdefg", null)); + } - [Fact] - public async Task InvokesBasketRepositoryFirstOrDefaultAsyncOnceIfAnonymousBasketNotExists() - { - var anonymousBasket = null as Basket; - var userBasket = new Basket(_existentUserBasketBuyerId); - _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) - .ReturnsAsync(anonymousBasket) - .ReturnsAsync(userBasket); - var basketService = new BasketService(_mockBasketRepo.Object, null); - await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId); - _mockBasketRepo.Verify(x => x.GetBySpecAsync(It.IsAny(), default), Times.Once); - } + [Fact] + public async Task InvokesBasketRepositoryFirstOrDefaultAsyncOnceIfAnonymousBasketNotExists() + { + var anonymousBasket = null as Basket; + var userBasket = new Basket(_existentUserBasketBuyerId); + _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) + .ReturnsAsync(anonymousBasket) + .ReturnsAsync(userBasket); + var basketService = new BasketService(_mockBasketRepo.Object, null); + await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId); + _mockBasketRepo.Verify(x => x.GetBySpecAsync(It.IsAny(), default), Times.Once); + } - [Fact] - public async Task TransferAnonymousBasketItemsWhilePreservingExistingUserBasketItems() - { - var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId); - anonymousBasket.AddItem(1, 10, 1); - anonymousBasket.AddItem(3, 55, 7); - var userBasket = new Basket(_existentUserBasketBuyerId); - userBasket.AddItem(1, 10, 4); - userBasket.AddItem(2, 99, 3); - _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) - .ReturnsAsync(anonymousBasket) - .ReturnsAsync(userBasket); - var basketService = new BasketService(_mockBasketRepo.Object, null); - await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId); - _mockBasketRepo.Verify(x => x.UpdateAsync(userBasket, default), Times.Once); - Assert.Equal(3, userBasket.Items.Count); - Assert.Contains(userBasket.Items, x => x.CatalogItemId == 1 && x.UnitPrice == 10 && x.Quantity == 5); - Assert.Contains(userBasket.Items, x => x.CatalogItemId == 2 && x.UnitPrice == 99 && x.Quantity == 3); - Assert.Contains(userBasket.Items, x => x.CatalogItemId == 3 && x.UnitPrice == 55 && x.Quantity == 7); - } + [Fact] + public async Task TransferAnonymousBasketItemsWhilePreservingExistingUserBasketItems() + { + var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId); + anonymousBasket.AddItem(1, 10, 1); + anonymousBasket.AddItem(3, 55, 7); + var userBasket = new Basket(_existentUserBasketBuyerId); + userBasket.AddItem(1, 10, 4); + userBasket.AddItem(2, 99, 3); + _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) + .ReturnsAsync(anonymousBasket) + .ReturnsAsync(userBasket); + var basketService = new BasketService(_mockBasketRepo.Object, null); + await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId); + _mockBasketRepo.Verify(x => x.UpdateAsync(userBasket, default), Times.Once); + Assert.Equal(3, userBasket.Items.Count); + Assert.Contains(userBasket.Items, x => x.CatalogItemId == 1 && x.UnitPrice == 10 && x.Quantity == 5); + Assert.Contains(userBasket.Items, x => x.CatalogItemId == 2 && x.UnitPrice == 99 && x.Quantity == 3); + Assert.Contains(userBasket.Items, x => x.CatalogItemId == 3 && x.UnitPrice == 55 && x.Quantity == 7); + } - [Fact] - public async Task RemovesAnonymousBasketAfterUpdatingUserBasket() - { - var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId); - var userBasket = new Basket(_existentUserBasketBuyerId); - _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) - .ReturnsAsync(anonymousBasket) - .ReturnsAsync(userBasket); - var basketService = new BasketService(_mockBasketRepo.Object, null); - await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId); - _mockBasketRepo.Verify(x => x.UpdateAsync(userBasket, default), Times.Once); - _mockBasketRepo.Verify(x => x.DeleteAsync(anonymousBasket, default), Times.Once); - } + [Fact] + public async Task RemovesAnonymousBasketAfterUpdatingUserBasket() + { + var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId); + var userBasket = new Basket(_existentUserBasketBuyerId); + _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) + .ReturnsAsync(anonymousBasket) + .ReturnsAsync(userBasket); + var basketService = new BasketService(_mockBasketRepo.Object, null); + await basketService.TransferBasketAsync(_nonexistentAnonymousBasketBuyerId, _existentUserBasketBuyerId); + _mockBasketRepo.Verify(x => x.UpdateAsync(userBasket, default), Times.Once); + _mockBasketRepo.Verify(x => x.DeleteAsync(anonymousBasket, default), Times.Once); + } - [Fact] - public async Task CreatesNewUserBasketIfNotExists() - { - var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId); - var userBasket = null as Basket; - _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) - .ReturnsAsync(anonymousBasket) - .ReturnsAsync(userBasket); - var basketService = new BasketService(_mockBasketRepo.Object, null); - await basketService.TransferBasketAsync(_existentAnonymousBasketBuyerId, _nonexistentUserBasketBuyerId); - _mockBasketRepo.Verify(x => x.AddAsync(It.Is(x => x.BuyerId == _nonexistentUserBasketBuyerId), default), Times.Once); - } + [Fact] + public async Task CreatesNewUserBasketIfNotExists() + { + var anonymousBasket = new Basket(_existentAnonymousBasketBuyerId); + var userBasket = null as Basket; + _mockBasketRepo.SetupSequence(x => x.GetBySpecAsync(It.IsAny(), default)) + .ReturnsAsync(anonymousBasket) + .ReturnsAsync(userBasket); + var basketService = new BasketService(_mockBasketRepo.Object, null); + await basketService.TransferBasketAsync(_existentAnonymousBasketBuyerId, _nonexistentUserBasketBuyerId); + _mockBasketRepo.Verify(x => x.AddAsync(It.Is(x => x.BuyerId == _nonexistentUserBasketBuyerId), default), Times.Once); } } diff --git a/tests/UnitTests/ApplicationCore/Specifications/BasketWithItemsSpecification.cs b/tests/UnitTests/ApplicationCore/Specifications/BasketWithItemsSpecification.cs index ee8ec534d..d2e342ac0 100644 --- a/tests/UnitTests/ApplicationCore/Specifications/BasketWithItemsSpecification.cs +++ b/tests/UnitTests/ApplicationCore/Specifications/BasketWithItemsSpecification.cs @@ -1,76 +1,75 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; +using System.Collections.Generic; +using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Moq; -using System.Collections.Generic; -using System.Linq; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications; + +public class BasketWithItems { - public class BasketWithItems - { - private readonly int _testBasketId = 123; - private readonly string _buyerId = "Test buyerId"; + private readonly int _testBasketId = 123; + private readonly string _buyerId = "Test buyerId"; - [Fact] - public void MatchesBasketWithGivenBasketId() - { - var spec = new BasketWithItemsSpecification(_testBasketId); + [Fact] + public void MatchesBasketWithGivenBasketId() + { + var spec = new BasketWithItemsSpecification(_testBasketId); - var result = spec.Evaluate(GetTestBasketCollection()).FirstOrDefault(); + var result = spec.Evaluate(GetTestBasketCollection()).FirstOrDefault(); - Assert.NotNull(result); - Assert.Equal(_testBasketId, result.Id); - } + Assert.NotNull(result); + Assert.Equal(_testBasketId, result.Id); + } - [Fact] - public void MatchesNoBasketsIfBasketIdNotPresent() - { - int badBasketId = -1; - var spec = new BasketWithItemsSpecification(badBasketId); + [Fact] + public void MatchesNoBasketsIfBasketIdNotPresent() + { + int badBasketId = -1; + var spec = new BasketWithItemsSpecification(badBasketId); - var result = spec.Evaluate(GetTestBasketCollection()).Any(); + var result = spec.Evaluate(GetTestBasketCollection()).Any(); - Assert.False(result); - } + Assert.False(result); + } - [Fact] - public void MatchesBasketWithGivenBuyerId() - { - var spec = new BasketWithItemsSpecification(_buyerId); + [Fact] + public void MatchesBasketWithGivenBuyerId() + { + var spec = new BasketWithItemsSpecification(_buyerId); - var result = spec.Evaluate(GetTestBasketCollection()).FirstOrDefault(); + var result = spec.Evaluate(GetTestBasketCollection()).FirstOrDefault(); - Assert.NotNull(result); - Assert.Equal(_buyerId, result.BuyerId); - } + Assert.NotNull(result); + Assert.Equal(_buyerId, result.BuyerId); + } - [Fact] - public void MatchesNoBasketsIfBuyerIdNotPresent() - { - string badBuyerId = "badBuyerId"; - var spec = new BasketWithItemsSpecification(badBuyerId); + [Fact] + public void MatchesNoBasketsIfBuyerIdNotPresent() + { + string badBuyerId = "badBuyerId"; + var spec = new BasketWithItemsSpecification(badBuyerId); - var result = spec.Evaluate(GetTestBasketCollection()).Any(); + var result = spec.Evaluate(GetTestBasketCollection()).Any(); - Assert.False(result); - } + Assert.False(result); + } - public List GetTestBasketCollection() - { - var basket1Mock = new Mock(_buyerId); - basket1Mock.SetupGet(s => s.Id).Returns(1); - var basket2Mock = new Mock(_buyerId); - basket2Mock.SetupGet(s => s.Id).Returns(2); - var basket3Mock = new Mock(_buyerId); - basket3Mock.SetupGet(s => s.Id).Returns(_testBasketId); + public List GetTestBasketCollection() + { + var basket1Mock = new Mock(_buyerId); + basket1Mock.SetupGet(s => s.Id).Returns(1); + var basket2Mock = new Mock(_buyerId); + basket2Mock.SetupGet(s => s.Id).Returns(2); + var basket3Mock = new Mock(_buyerId); + basket3Mock.SetupGet(s => s.Id).Returns(_testBasketId); - return new List() + return new List() { basket1Mock.Object, basket2Mock.Object, basket3Mock.Object }; - } } } diff --git a/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs b/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs index 7592c19c8..208593e9d 100644 --- a/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs +++ b/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs @@ -1,48 +1,47 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Entities; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications; + +public class CatalogFilterPaginatedSpecification { - public class CatalogFilterPaginatedSpecification + [Fact] + public void ReturnsAllCatalogItems() { - [Fact] - public void ReturnsAllCatalogItems() - { - var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterPaginatedSpecification(0, 10, null, null); - - var result = GetTestCollection() - .AsQueryable() - .Where(spec.WhereExpressions.FirstOrDefault()); - - Assert.NotNull(result); - Assert.Equal(4, result.ToList().Count); - } - - [Fact] - public void Returns2CatalogItemsWithSameBrandAndTypeId() - { - var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterPaginatedSpecification(0, 10, 1, 1); - - var result = GetTestCollection() - .AsQueryable() - .Where(spec.WhereExpressions.FirstOrDefault()); - - Assert.NotNull(result); - Assert.Equal(2, result.ToList().Count); - } - - private List GetTestCollection() - { - var catalogItemList = new List(); - - catalogItemList.Add(new CatalogItem(1, 1, "Item 1", "Item 1", 1.00m, "TestUri1")); - catalogItemList.Add(new CatalogItem(1, 1, "Item 1.5", "Item 1.5", 1.50m, "TestUri1")); - catalogItemList.Add(new CatalogItem(2, 2, "Item 2", "Item 2", 2.00m, "TestUri2")); - catalogItemList.Add(new CatalogItem(3, 3, "Item 3", "Item 3", 3.00m, "TestUri3")); - - return catalogItemList; - } + var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterPaginatedSpecification(0, 10, null, null); + + var result = GetTestCollection() + .AsQueryable() + .Where(spec.WhereExpressions.FirstOrDefault()); + + Assert.NotNull(result); + Assert.Equal(4, result.ToList().Count); + } + + [Fact] + public void Returns2CatalogItemsWithSameBrandAndTypeId() + { + var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterPaginatedSpecification(0, 10, 1, 1); + + var result = GetTestCollection() + .AsQueryable() + .Where(spec.WhereExpressions.FirstOrDefault()); + + Assert.NotNull(result); + Assert.Equal(2, result.ToList().Count); + } + + private List GetTestCollection() + { + var catalogItemList = new List(); + + catalogItemList.Add(new CatalogItem(1, 1, "Item 1", "Item 1", 1.00m, "TestUri1")); + catalogItemList.Add(new CatalogItem(1, 1, "Item 1.5", "Item 1.5", 1.50m, "TestUri1")); + catalogItemList.Add(new CatalogItem(2, 2, "Item 2", "Item 2", 2.00m, "TestUri2")); + catalogItemList.Add(new CatalogItem(3, 3, "Item 3", "Item 3", 3.00m, "TestUri3")); + + return catalogItemList; } } diff --git a/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterSpecification.cs b/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterSpecification.cs index 8b9c5a75b..f5d01a6c4 100644 --- a/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterSpecification.cs +++ b/tests/UnitTests/ApplicationCore/Specifications/CatalogFilterSpecification.cs @@ -1,41 +1,40 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Entities; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications; + +public class CatalogFilterSpecification { - public class CatalogFilterSpecification + [Theory] + [InlineData(null, null, 5)] + [InlineData(1, null, 3)] + [InlineData(2, null, 2)] + [InlineData(null, 1, 2)] + [InlineData(null, 3, 1)] + [InlineData(1, 3, 1)] + [InlineData(2, 3, 0)] + public void MatchesExpectedNumberOfItems(int? brandId, int? typeId, int expectedCount) { - [Theory] - [InlineData(null, null, 5)] - [InlineData(1, null, 3)] - [InlineData(2, null, 2)] - [InlineData(null, 1, 2)] - [InlineData(null, 3, 1)] - [InlineData(1, 3, 1)] - [InlineData(2, 3, 0)] - public void MatchesExpectedNumberOfItems(int? brandId, int? typeId, int expectedCount) - { - var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterSpecification(brandId, typeId); + var spec = new eShopWeb.ApplicationCore.Specifications.CatalogFilterSpecification(brandId, typeId); - var result = GetTestItemCollection() - .AsQueryable() - .Where(spec.WhereExpressions.FirstOrDefault()); + var result = GetTestItemCollection() + .AsQueryable() + .Where(spec.WhereExpressions.FirstOrDefault()); - Assert.Equal(expectedCount, result.Count()); - } + Assert.Equal(expectedCount, result.Count()); + } - public List GetTestItemCollection() - { - return new List() + public List GetTestItemCollection() + { + return new List() { new CatalogItem(1, 1, "Description", "Name", 0, "FakePath"), new CatalogItem(2, 1, "Description", "Name", 0, "FakePath"), new CatalogItem(3, 1, "Description", "Name", 0, "FakePath"), new CatalogItem(1, 2, "Description", "Name", 0, "FakePath"), - new CatalogItem(2, 2, "Description", "Name", 0, "FakePath"), + new CatalogItem(2, 2, "Description", "Name", 0, "FakePath"), }; - } } } diff --git a/tests/UnitTests/ApplicationCore/Specifications/CatalogItemsSpecification.cs b/tests/UnitTests/ApplicationCore/Specifications/CatalogItemsSpecification.cs index 0f8e4c598..84156ce3b 100644 --- a/tests/UnitTests/ApplicationCore/Specifications/CatalogItemsSpecification.cs +++ b/tests/UnitTests/ApplicationCore/Specifications/CatalogItemsSpecification.cs @@ -1,55 +1,54 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities; -using Moq; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Entities; +using Moq; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications; + +public class CatalogItemsSpecification { - public class CatalogItemsSpecification + [Fact] + public void MatchesSpecificCatalogItem() { - [Fact] - public void MatchesSpecificCatalogItem() - { - var catalogItemIds = new int[] { 1 }; - var spec = new eShopWeb.ApplicationCore.Specifications.CatalogItemsSpecification(catalogItemIds); + var catalogItemIds = new int[] { 1 }; + var spec = new eShopWeb.ApplicationCore.Specifications.CatalogItemsSpecification(catalogItemIds); - var result = GetTestCollection() - .AsQueryable() - .Where(spec.WhereExpressions.FirstOrDefault()); + var result = GetTestCollection() + .AsQueryable() + .Where(spec.WhereExpressions.FirstOrDefault()); - Assert.NotNull(result); - Assert.Single(result.ToList()); - } + Assert.NotNull(result); + Assert.Single(result.ToList()); + } - [Fact] - public void MatchesAllCatalogItems() - { - var catalogItemIds = new int[] { 1, 3 }; - var spec = new eShopWeb.ApplicationCore.Specifications.CatalogItemsSpecification(catalogItemIds); + [Fact] + public void MatchesAllCatalogItems() + { + var catalogItemIds = new int[] { 1, 3 }; + var spec = new eShopWeb.ApplicationCore.Specifications.CatalogItemsSpecification(catalogItemIds); - var result = GetTestCollection() - .AsQueryable() - .Where(spec.WhereExpressions.FirstOrDefault()); + var result = GetTestCollection() + .AsQueryable() + .Where(spec.WhereExpressions.FirstOrDefault()); - Assert.NotNull(result); - Assert.Equal(2, result.ToList().Count); - } + Assert.NotNull(result); + Assert.Equal(2, result.ToList().Count); + } - private List GetTestCollection() - { - var catalogItems = new List(); + private List GetTestCollection() + { + var catalogItems = new List(); - var mockCatalogItem1 = new Mock(1, 1, "Item 1 description", "Item 1", 1.5m, "Item1Uri"); - mockCatalogItem1.SetupGet(x => x.Id).Returns(1); + var mockCatalogItem1 = new Mock(1, 1, "Item 1 description", "Item 1", 1.5m, "Item1Uri"); + mockCatalogItem1.SetupGet(x => x.Id).Returns(1); - var mockCatalogItem3 = new Mock(3, 3, "Item 3 description", "Item 3", 3.5m, "Item3Uri"); - mockCatalogItem3.SetupGet(x => x.Id).Returns(3); + var mockCatalogItem3 = new Mock(3, 3, "Item 3 description", "Item 3", 3.5m, "Item3Uri"); + mockCatalogItem3.SetupGet(x => x.Id).Returns(3); - catalogItems.Add(mockCatalogItem1.Object); - catalogItems.Add(mockCatalogItem3.Object); + catalogItems.Add(mockCatalogItem1.Object); + catalogItems.Add(mockCatalogItem3.Object); - return catalogItems; - } + return catalogItems; } } diff --git a/tests/UnitTests/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs b/tests/UnitTests/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs index 2ef842055..f95d3b522 100644 --- a/tests/UnitTests/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs +++ b/tests/UnitTests/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs @@ -1,66 +1,65 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications +namespace Microsoft.eShopWeb.UnitTests.ApplicationCore.Specifications; + +public class CustomerOrdersWithItemsSpecification { - public class CustomerOrdersWithItemsSpecification - { - private readonly string _buyerId = "TestBuyerId"; - private Address _shipToAddress = new Address("Street", "City", "OH", "US", "11111"); + private readonly string _buyerId = "TestBuyerId"; + private Address _shipToAddress = new Address("Street", "City", "OH", "US", "11111"); - [Fact] - public void ReturnsOrderWithOrderedItem() - { - var spec = new eShopWeb.ApplicationCore.Specifications.CustomerOrdersWithItemsSpecification(_buyerId); + [Fact] + public void ReturnsOrderWithOrderedItem() + { + var spec = new eShopWeb.ApplicationCore.Specifications.CustomerOrdersWithItemsSpecification(_buyerId); - var result = GetTestCollection() - .AsQueryable() - .FirstOrDefault(spec.WhereExpressions.FirstOrDefault()); + var result = GetTestCollection() + .AsQueryable() + .FirstOrDefault(spec.WhereExpressions.FirstOrDefault()); - Assert.NotNull(result); - Assert.NotNull(result.OrderItems); - Assert.Equal(1, result.OrderItems.Count); - Assert.NotNull(result.OrderItems.FirstOrDefault().ItemOrdered); - } + Assert.NotNull(result); + Assert.NotNull(result.OrderItems); + Assert.Equal(1, result.OrderItems.Count); + Assert.NotNull(result.OrderItems.FirstOrDefault().ItemOrdered); + } - [Fact] - public void ReturnsAllOrderWithAllOrderedItem() - { - var spec = new eShopWeb.ApplicationCore.Specifications.CustomerOrdersWithItemsSpecification(_buyerId); + [Fact] + public void ReturnsAllOrderWithAllOrderedItem() + { + var spec = new eShopWeb.ApplicationCore.Specifications.CustomerOrdersWithItemsSpecification(_buyerId); - var result = GetTestCollection() - .AsQueryable() - .Where(spec.WhereExpressions.FirstOrDefault()) - .ToList(); + var result = GetTestCollection() + .AsQueryable() + .Where(spec.WhereExpressions.FirstOrDefault()) + .ToList(); - Assert.NotNull(result); - Assert.Equal(2, result.Count); - Assert.Equal(1, result[0].OrderItems.Count); - Assert.NotNull(result[0].OrderItems.FirstOrDefault().ItemOrdered); - Assert.Equal(2, result[1].OrderItems.Count); - Assert.NotNull(result[1].OrderItems.ToList()[0].ItemOrdered); - Assert.NotNull(result[1].OrderItems.ToList()[1].ItemOrdered); - } + Assert.NotNull(result); + Assert.Equal(2, result.Count); + Assert.Equal(1, result[0].OrderItems.Count); + Assert.NotNull(result[0].OrderItems.FirstOrDefault().ItemOrdered); + Assert.Equal(2, result[1].OrderItems.Count); + Assert.NotNull(result[1].OrderItems.ToList()[0].ItemOrdered); + Assert.NotNull(result[1].OrderItems.ToList()[1].ItemOrdered); + } - public List GetTestCollection() - { - var ordersList = new List(); + public List GetTestCollection() + { + var ordersList = new List(); - ordersList.Add(new Order(_buyerId, _shipToAddress, - new List - { + ordersList.Add(new Order(_buyerId, _shipToAddress, + new List + { new OrderItem(new CatalogItemOrdered(1, "Product1", "testurl"), 10.50m, 1) - })); - ordersList.Add(new Order(_buyerId, _shipToAddress, - new List - { + })); + ordersList.Add(new Order(_buyerId, _shipToAddress, + new List + { new OrderItem(new CatalogItemOrdered(2, "Product2", "testurl"), 15.50m, 2), new OrderItem(new CatalogItemOrdered(2, "Product3", "testurl"), 20.50m, 1) - })); + })); - return ordersList; - } + return ordersList; } } diff --git a/tests/UnitTests/Builders/AddressBuilder.cs b/tests/UnitTests/Builders/AddressBuilder.cs index 295e9e7bb..20e40c34f 100644 --- a/tests/UnitTests/Builders/AddressBuilder.cs +++ b/tests/UnitTests/Builders/AddressBuilder.cs @@ -1,28 +1,27 @@ using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.UnitTests.Builders +namespace Microsoft.eShopWeb.UnitTests.Builders; + +public class AddressBuilder { - public class AddressBuilder - { - private Address _address; - public string TestStreet => "123 Main St."; - public string TestCity => "Kent"; - public string TestState => "OH"; - public string TestCountry => "USA"; - public string TestZipCode => "44240"; + private Address _address; + public string TestStreet => "123 Main St."; + public string TestCity => "Kent"; + public string TestState => "OH"; + public string TestCountry => "USA"; + public string TestZipCode => "44240"; - public AddressBuilder() - { - _address = WithDefaultValues(); - } - public Address Build() - { - return _address; - } - public Address WithDefaultValues() - { - _address = new Address(TestStreet, TestCity, TestState, TestCountry, TestZipCode); - return _address; - } + public AddressBuilder() + { + _address = WithDefaultValues(); + } + public Address Build() + { + return _address; + } + public Address WithDefaultValues() + { + _address = new Address(TestStreet, TestCity, TestState, TestCountry, TestZipCode); + return _address; } } diff --git a/tests/UnitTests/Builders/BasketBuilder.cs b/tests/UnitTests/Builders/BasketBuilder.cs index 40860e9bd..08dd04a44 100644 --- a/tests/UnitTests/Builders/BasketBuilder.cs +++ b/tests/UnitTests/Builders/BasketBuilder.cs @@ -1,40 +1,39 @@ using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Moq; -namespace Microsoft.eShopWeb.UnitTests.Builders +namespace Microsoft.eShopWeb.UnitTests.Builders; + +public class BasketBuilder { - public class BasketBuilder + private Basket _basket; + public string BasketBuyerId => "testbuyerId@test.com"; + + public int BasketId => 1; + + public BasketBuilder() + { + _basket = WithNoItems(); + } + + public Basket Build() + { + return _basket; + } + + public Basket WithNoItems() + { + var basketMock = new Mock(BasketBuyerId); + basketMock.SetupGet(s => s.Id).Returns(BasketId); + + _basket = basketMock.Object; + return _basket; + } + + public Basket WithOneBasketItem() { - private Basket _basket; - public string BasketBuyerId => "testbuyerId@test.com"; - - public int BasketId => 1; - - public BasketBuilder() - { - _basket = WithNoItems(); - } - - public Basket Build() - { - return _basket; - } - - public Basket WithNoItems() - { - var basketMock = new Mock(BasketBuyerId); - basketMock.SetupGet(s => s.Id).Returns(BasketId); - - _basket = basketMock.Object; - return _basket; - } - - public Basket WithOneBasketItem() - { - var basketMock = new Mock(BasketBuyerId); - _basket = basketMock.Object; - _basket.AddItem(2, 3.40m, 4); - return _basket; - } + var basketMock = new Mock(BasketBuyerId); + _basket = basketMock.Object; + _basket.AddItem(2, 3.40m, 4); + return _basket; } } diff --git a/tests/UnitTests/Builders/OrderBuilder.cs b/tests/UnitTests/Builders/OrderBuilder.cs index 17c0b1197..bb9ee61d9 100644 --- a/tests/UnitTests/Builders/OrderBuilder.cs +++ b/tests/UnitTests/Builders/OrderBuilder.cs @@ -1,48 +1,47 @@ -using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -using System.Collections.Generic; +using System.Collections.Generic; +using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; -namespace Microsoft.eShopWeb.UnitTests.Builders +namespace Microsoft.eShopWeb.UnitTests.Builders; + +public class OrderBuilder { - public class OrderBuilder - { - private Order _order; - public string TestBuyerId => "12345"; - public int TestCatalogItemId => 234; - public string TestProductName => "Test Product Name"; - public string TestPictureUri => "http://test.com/image.jpg"; - public decimal TestUnitPrice = 1.23m; - public int TestUnits = 3; - public CatalogItemOrdered TestCatalogItemOrdered { get; } + private Order _order; + public string TestBuyerId => "12345"; + public int TestCatalogItemId => 234; + public string TestProductName => "Test Product Name"; + public string TestPictureUri => "http://test.com/image.jpg"; + public decimal TestUnitPrice = 1.23m; + public int TestUnits = 3; + public CatalogItemOrdered TestCatalogItemOrdered { get; } - public OrderBuilder() - { - TestCatalogItemOrdered = new CatalogItemOrdered(TestCatalogItemId, TestProductName, TestPictureUri); - _order = WithDefaultValues(); - } + public OrderBuilder() + { + TestCatalogItemOrdered = new CatalogItemOrdered(TestCatalogItemId, TestProductName, TestPictureUri); + _order = WithDefaultValues(); + } - public Order Build() - { - return _order; - } + public Order Build() + { + return _order; + } - public Order WithDefaultValues() - { - var orderItem = new OrderItem(TestCatalogItemOrdered, TestUnitPrice, TestUnits); - var itemList = new List() { orderItem }; - _order = new Order(TestBuyerId, new AddressBuilder().WithDefaultValues(), itemList); - return _order; - } + public Order WithDefaultValues() + { + var orderItem = new OrderItem(TestCatalogItemOrdered, TestUnitPrice, TestUnits); + var itemList = new List() { orderItem }; + _order = new Order(TestBuyerId, new AddressBuilder().WithDefaultValues(), itemList); + return _order; + } - public Order WithNoItems() - { - _order = new Order(TestBuyerId, new AddressBuilder().WithDefaultValues(), new List()); - return _order; - } + public Order WithNoItems() + { + _order = new Order(TestBuyerId, new AddressBuilder().WithDefaultValues(), new List()); + return _order; + } - public Order WithItems(List items) - { - _order = new Order(TestBuyerId, new AddressBuilder().WithDefaultValues(), items); - return _order; - } + public Order WithItems(List items) + { + _order = new Order(TestBuyerId, new AddressBuilder().WithDefaultValues(), items); + return _order; } } diff --git a/tests/UnitTests/MediatorHandlers/OrdersTests/GetMyOrders.cs b/tests/UnitTests/MediatorHandlers/OrdersTests/GetMyOrders.cs index c693b4023..e0bd0038f 100644 --- a/tests/UnitTests/MediatorHandlers/OrdersTests/GetMyOrders.cs +++ b/tests/UnitTests/MediatorHandlers/OrdersTests/GetMyOrders.cs @@ -1,39 +1,38 @@ -using Ardalis.Specification; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.Web.Features.MyOrders; using Moq; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.MediatorHandlers.OrdersTests +namespace Microsoft.eShopWeb.UnitTests.MediatorHandlers.OrdersTests; + +public class GetMyOrders { - public class GetMyOrders - { - private readonly Mock> _mockOrderRepository; + private readonly Mock> _mockOrderRepository; - public GetMyOrders() - { - var item = new OrderItem(new CatalogItemOrdered(1, "ProductName", "URI"), 10.00m, 10); - var address = new Address(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()); - Order order = new Order("buyerId", address, new List { item }); + public GetMyOrders() + { + var item = new OrderItem(new CatalogItemOrdered(1, "ProductName", "URI"), 10.00m, 10); + var address = new Address(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()); + Order order = new Order("buyerId", address, new List { item }); - _mockOrderRepository = new Mock>(); - _mockOrderRepository.Setup(x => x.ListAsync(It.IsAny>(),default)).ReturnsAsync(new List { order }); - } + _mockOrderRepository = new Mock>(); + _mockOrderRepository.Setup(x => x.ListAsync(It.IsAny>(), default)).ReturnsAsync(new List { order }); + } - [Fact] - public async Task NotReturnNullIfOrdersArePresIent() - { - var request = new eShopWeb.Web.Features.MyOrders.GetMyOrders("SomeUserName"); + [Fact] + public async Task NotReturnNullIfOrdersArePresIent() + { + var request = new eShopWeb.Web.Features.MyOrders.GetMyOrders("SomeUserName"); - var handler = new GetMyOrdersHandler(_mockOrderRepository.Object); + var handler = new GetMyOrdersHandler(_mockOrderRepository.Object); - var result = await handler.Handle(request, CancellationToken.None); + var result = await handler.Handle(request, CancellationToken.None); - Assert.NotNull(result); - } + Assert.NotNull(result); } } diff --git a/tests/UnitTests/MediatorHandlers/OrdersTests/GetOrderDetails.cs b/tests/UnitTests/MediatorHandlers/OrdersTests/GetOrderDetails.cs index 76afcecc1..ec4cadde8 100644 --- a/tests/UnitTests/MediatorHandlers/OrdersTests/GetOrderDetails.cs +++ b/tests/UnitTests/MediatorHandlers/OrdersTests/GetOrderDetails.cs @@ -1,41 +1,40 @@ -using Ardalis.Specification; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.Specification; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Microsoft.eShopWeb.Web.Features.OrderDetails; using Moq; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.MediatorHandlers.OrdersTests +namespace Microsoft.eShopWeb.UnitTests.MediatorHandlers.OrdersTests; + +public class GetOrderDetails { - public class GetOrderDetails - { - private readonly Mock> _mockOrderRepository; + private readonly Mock> _mockOrderRepository; - public GetOrderDetails() - { - var item = new OrderItem(new CatalogItemOrdered(1, "ProductName", "URI"), 10.00m, 10); - var address = new Address(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()); - Order order = new Order("buyerId", address, new List { item }); + public GetOrderDetails() + { + var item = new OrderItem(new CatalogItemOrdered(1, "ProductName", "URI"), 10.00m, 10); + var address = new Address(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()); + Order order = new Order("buyerId", address, new List { item }); - _mockOrderRepository = new Mock>(); - _mockOrderRepository.Setup(x => x.GetBySpecAsync(It.IsAny(),default)) - .ReturnsAsync(order); - } + _mockOrderRepository = new Mock>(); + _mockOrderRepository.Setup(x => x.GetBySpecAsync(It.IsAny(), default)) + .ReturnsAsync(order); + } - [Fact] - public async Task NotBeNullIfOrderExists() - { - var request = new eShopWeb.Web.Features.OrderDetails.GetOrderDetails("SomeUserName", 0); + [Fact] + public async Task NotBeNullIfOrderExists() + { + var request = new eShopWeb.Web.Features.OrderDetails.GetOrderDetails("SomeUserName", 0); - var handler = new GetOrderDetailsHandler(_mockOrderRepository.Object); + var handler = new GetOrderDetailsHandler(_mockOrderRepository.Object); - var result = await handler.Handle(request, CancellationToken.None); + var result = await handler.Handle(request, CancellationToken.None); - Assert.NotNull(result); - } + Assert.NotNull(result); } } diff --git a/tests/UnitTests/UnitTests.csproj b/tests/UnitTests/UnitTests.csproj index def620774..b9219222e 100644 --- a/tests/UnitTests/UnitTests.csproj +++ b/tests/UnitTests/UnitTests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Microsoft.eShopWeb.UnitTests false diff --git a/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateBrandsCacheKey.cs b/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateBrandsCacheKey.cs index 4c2c89614..93164f358 100644 --- a/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateBrandsCacheKey.cs +++ b/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateBrandsCacheKey.cs @@ -1,16 +1,15 @@ using Microsoft.eShopWeb.Web.Extensions; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.Web.Extensions.CacheHelpersTests +namespace Microsoft.eShopWeb.UnitTests.Web.Extensions.CacheHelpersTests; + +public class GenerateBrandsCacheKey { - public class GenerateBrandsCacheKey + [Fact] + public void ReturnsBrandsCacheKey() { - [Fact] - public void ReturnsBrandsCacheKey() - { - var result = CacheHelpers.GenerateBrandsCacheKey(); + var result = CacheHelpers.GenerateBrandsCacheKey(); - Assert.Equal("brands", result); - } + Assert.Equal("brands", result); } } diff --git a/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateCatalogItemCacheKey.cs b/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateCatalogItemCacheKey.cs index a40f61b64..f4761b38a 100644 --- a/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateCatalogItemCacheKey.cs +++ b/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateCatalogItemCacheKey.cs @@ -2,20 +2,19 @@ using Microsoft.eShopWeb.Web.Extensions; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.Web.Extensions.CacheHelpersTests +namespace Microsoft.eShopWeb.UnitTests.Web.Extensions.CacheHelpersTests; + +public class GenerateCatalogItemCacheKey { - public class GenerateCatalogItemCacheKey + [Fact] + public void ReturnsCatalogItemCacheKey() { - [Fact] - public void ReturnsCatalogItemCacheKey() - { - var pageIndex = 0; - int? brandId = null; - int? typeId = null; + var pageIndex = 0; + int? brandId = null; + int? typeId = null; - var result = CacheHelpers.GenerateCatalogItemCacheKey(pageIndex, Constants.ITEMS_PER_PAGE, brandId, typeId); + var result = CacheHelpers.GenerateCatalogItemCacheKey(pageIndex, Constants.ITEMS_PER_PAGE, brandId, typeId); - Assert.Equal("items-0-10--", result); - } + Assert.Equal("items-0-10--", result); } } diff --git a/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateTypesCacheKey.cs b/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateTypesCacheKey.cs index 96bdd2abe..64072930d 100644 --- a/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateTypesCacheKey.cs +++ b/tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateTypesCacheKey.cs @@ -1,16 +1,15 @@ using Microsoft.eShopWeb.Web.Extensions; using Xunit; -namespace Microsoft.eShopWeb.UnitTests.Web.Extensions.CacheHelpersTests +namespace Microsoft.eShopWeb.UnitTests.Web.Extensions.CacheHelpersTests; + +public class GenerateTypesCacheKey { - public class GenerateTypesCacheKey + [Fact] + public void ReturnsTypesCacheKey() { - [Fact] - public void ReturnsTypesCacheKey() - { - var result = CacheHelpers.GenerateTypesCacheKey(); + var result = CacheHelpers.GenerateTypesCacheKey(); - Assert.Equal("types", result); - } + Assert.Equal("types", result); } }