Showing preview only (718K chars total). Download the full file or copy to clipboard to get everything.
Repository: MicrosoftLearning/eShopOnWeb
Branch: main
Commit: 20179a783d34
Files: 424
Total size: 611.6 KB
Directory structure:
gitextract_58gu5nma/
├── .ado/
│ ├── eshoponweb-cd-aci.yml
│ ├── eshoponweb-cd-webapp-code.yml
│ ├── eshoponweb-cd-webapp-docker.yml
│ ├── eshoponweb-cd-webapp-dockercompose.yml
│ ├── eshoponweb-cd-windows-cm.yml
│ ├── eshoponweb-ci-docker.yml
│ ├── eshoponweb-ci-dockercompose.yml
│ ├── eshoponweb-ci-mend.yml
│ ├── eshoponweb-ci-pr.yml
│ ├── eshoponweb-ci.yml
│ └── eshoponweb-sonar-ci.yml
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── dotnetcore.yml
│ ├── eshoponweb-cicd.yml
│ └── richnav.yml
├── .gitignore
├── .vscode/
│ ├── extensions.json
│ ├── launch.json
│ └── tasks.json
├── CodeCoverage.runsettings
├── Directory.Packages.props
├── LICENSE
├── MTT-Notes.md
├── README.md
├── azure.yaml
├── docker-compose-webapp.yml
├── docker-compose.dcproj
├── docker-compose.override.yml
├── docker-compose.yml
├── eShopOnWeb.sln
├── global.json
├── infra/
│ ├── abbreviations.json
│ ├── aci.bicep
│ ├── acr.bicep
│ ├── core/
│ │ ├── database/
│ │ │ └── sqlserver/
│ │ │ └── sqlserver.bicep
│ │ ├── host/
│ │ │ ├── appservice.bicep
│ │ │ └── appserviceplan.bicep
│ │ └── security/
│ │ ├── keyvault-access.bicep
│ │ └── keyvault.bicep
│ ├── main.bicep
│ ├── main.parameters.json
│ ├── simple-windows-vm.bicep
│ ├── webapp-docker.bicep
│ ├── webapp-to-acr-roleassignment.bicep
│ └── webapp.bicep
├── src/
│ ├── ApplicationCore/
│ │ ├── ApplicationCore.csproj
│ │ ├── CatalogSettings.cs
│ │ ├── Constants/
│ │ │ └── AuthorizationConstants.cs
│ │ ├── Entities/
│ │ │ ├── BaseEntity.cs
│ │ │ ├── BasketAggregate/
│ │ │ │ ├── Basket.cs
│ │ │ │ └── BasketItem.cs
│ │ │ ├── BuyerAggregate/
│ │ │ │ ├── Buyer.cs
│ │ │ │ └── PaymentMethod.cs
│ │ │ ├── CatalogBrand.cs
│ │ │ ├── CatalogItem.cs
│ │ │ ├── CatalogType.cs
│ │ │ ├── EshopDiagram.cd
│ │ │ └── OrderAggregate/
│ │ │ ├── Address.cs
│ │ │ ├── CatalogItemOrdered.cs
│ │ │ ├── Order.cs
│ │ │ └── OrderItem.cs
│ │ ├── Exceptions/
│ │ │ ├── BasketNotFoundException.cs
│ │ │ ├── DuplicateException.cs
│ │ │ └── EmptyBasketOnCheckoutException.cs
│ │ ├── Extensions/
│ │ │ ├── GuardExtensions.cs
│ │ │ └── JsonExtensions.cs
│ │ ├── Interfaces/
│ │ │ ├── IAggregateRoot.cs
│ │ │ ├── IAppLogger.cs
│ │ │ ├── IBasketQueryService.cs
│ │ │ ├── IBasketService.cs
│ │ │ ├── IEmailSender.cs
│ │ │ ├── IOrderService.cs
│ │ │ ├── IReadRepository.cs
│ │ │ ├── IRepository.cs
│ │ │ ├── ITokenClaimsService.cs
│ │ │ └── IUriComposer.cs
│ │ ├── Services/
│ │ │ ├── BasketService.cs
│ │ │ ├── OrderService.cs
│ │ │ └── UriComposer.cs
│ │ └── Specifications/
│ │ ├── BasketWithItemsSpecification.cs
│ │ ├── CatalogFilterPaginatedSpecification.cs
│ │ ├── CatalogFilterSpecification.cs
│ │ ├── CatalogItemNameSpecification.cs
│ │ ├── CatalogItemsSpecification.cs
│ │ ├── CustomerOrdersSpecification.cs
│ │ ├── CustomerOrdersWithItemsSpecification.cs
│ │ └── OrderWithItemsByIdSpec.cs
│ ├── BlazorAdmin/
│ │ ├── App.razor
│ │ ├── BlazorAdmin.csproj
│ │ ├── CustomAuthStateProvider.cs
│ │ ├── Helpers/
│ │ │ ├── BlazorComponent.cs
│ │ │ ├── BlazorLayoutComponent.cs
│ │ │ ├── RefreshBroadcast.cs
│ │ │ └── ToastComponent.cs
│ │ ├── JavaScript/
│ │ │ ├── Cookies.cs
│ │ │ ├── Css.cs
│ │ │ ├── JSInteropConstants.cs
│ │ │ └── Route.cs
│ │ ├── Pages/
│ │ │ ├── CatalogItemPage/
│ │ │ │ ├── Create.razor
│ │ │ │ ├── Delete.razor
│ │ │ │ ├── Details.razor
│ │ │ │ ├── Edit.razor
│ │ │ │ ├── List.razor
│ │ │ │ └── List.razor.cs
│ │ │ └── Logout.razor
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── launchSettings.json
│ │ ├── Services/
│ │ │ ├── CacheEntry.cs
│ │ │ ├── CachedCatalogItemServiceDecorator.cs
│ │ │ ├── CachedCatalogLookupDataServiceDecorator .cs
│ │ │ ├── CatalogItemService.cs
│ │ │ ├── CatalogLookupDataService.cs
│ │ │ ├── HttpService.cs
│ │ │ └── ToastService.cs
│ │ ├── ServicesConfiguration.cs
│ │ ├── Shared/
│ │ │ ├── CustomInputSelect.cs
│ │ │ ├── MainLayout.razor
│ │ │ ├── NavMenu.razor
│ │ │ ├── RedirectToLogin.razor
│ │ │ ├── Spinner.razor
│ │ │ └── Toast.razor
│ │ ├── _Imports.razor
│ │ └── wwwroot/
│ │ ├── appsettings.Development.json
│ │ ├── appsettings.Docker.json
│ │ ├── appsettings.json
│ │ └── css/
│ │ ├── admin.css
│ │ └── open-iconic/
│ │ ├── FONT-LICENSE
│ │ ├── ICON-LICENSE
│ │ ├── README.md
│ │ └── font/
│ │ └── fonts/
│ │ └── open-iconic.otf
│ ├── BlazorShared/
│ │ ├── Attributes/
│ │ │ └── EndpointAttribute.cs
│ │ ├── Authorization/
│ │ │ ├── ClaimValue.cs
│ │ │ ├── Constants.cs
│ │ │ └── UserInfo.cs
│ │ ├── BaseUrlConfiguration.cs
│ │ ├── BlazorShared.csproj
│ │ ├── Interfaces/
│ │ │ ├── ICatalogItemService.cs
│ │ │ ├── ICatalogLookupDataService.cs
│ │ │ └── ILookupDataResponse.cs
│ │ └── Models/
│ │ ├── CatalogBrand.cs
│ │ ├── CatalogBrandResponse.cs
│ │ ├── CatalogItem.cs
│ │ ├── CatalogType.cs
│ │ ├── CatalogTypeResponse.cs
│ │ ├── CreateCatalogItemRequest.cs
│ │ ├── CreateCatalogItemResponse.cs
│ │ ├── DeleteCatalogItemResponse.cs
│ │ ├── EditCatalogItemResponse.cs
│ │ ├── ErrorDetails.cs
│ │ ├── LookupData.cs
│ │ └── PagedCatalogItemResponse.cs
│ ├── Infrastructure/
│ │ ├── Data/
│ │ │ ├── CatalogContext.cs
│ │ │ ├── CatalogContextSeed.cs
│ │ │ ├── Config/
│ │ │ │ ├── BasketConfiguration.cs
│ │ │ │ ├── BasketItemConfiguration.cs
│ │ │ │ ├── CatalogBrandConfiguration.cs
│ │ │ │ ├── CatalogItemConfiguration.cs
│ │ │ │ ├── CatalogTypeConfiguration.cs
│ │ │ │ ├── OrderConfiguration.cs
│ │ │ │ └── OrderItemConfiguration.cs
│ │ │ ├── EfRepository.cs
│ │ │ ├── FileItem.cs
│ │ │ ├── Migrations/
│ │ │ │ ├── 20201202111507_InitialModel.Designer.cs
│ │ │ │ ├── 20201202111507_InitialModel.cs
│ │ │ │ ├── 20211026175614_FixBuyerId.Designer.cs
│ │ │ │ ├── 20211026175614_FixBuyerId.cs
│ │ │ │ ├── 20211231093753_FixShipToAddress.Designer.cs
│ │ │ │ ├── 20211231093753_FixShipToAddress.cs
│ │ │ │ └── CatalogContextModelSnapshot.cs
│ │ │ └── Queries/
│ │ │ └── BasketQueryService.cs
│ │ ├── Dependencies.cs
│ │ ├── Identity/
│ │ │ ├── AppIdentityDbContext.cs
│ │ │ ├── AppIdentityDbContextSeed.cs
│ │ │ ├── ApplicationUser.cs
│ │ │ ├── IdentityTokenClaimService.cs
│ │ │ ├── Migrations/
│ │ │ │ ├── 20201202111612_InitialIdentityModel.Designer.cs
│ │ │ │ ├── 20201202111612_InitialIdentityModel.cs
│ │ │ │ └── AppIdentityDbContextModelSnapshot.cs
│ │ │ └── UserNotFoundException.cs
│ │ ├── Infrastructure.csproj
│ │ ├── Logging/
│ │ │ └── LoggerAdapter.cs
│ │ └── Services/
│ │ └── EmailSender.cs
│ ├── PublicApi/
│ │ ├── AuthEndpoints/
│ │ │ ├── AuthenticateEndpoint.AuthenticateRequest.cs
│ │ │ ├── AuthenticateEndpoint.AuthenticateResponse.cs
│ │ │ ├── AuthenticateEndpoint.ClaimValue.cs
│ │ │ ├── AuthenticateEndpoint.UserInfo.cs
│ │ │ └── AuthenticateEndpoint.cs
│ │ ├── BaseMessage.cs
│ │ ├── BaseRequest.cs
│ │ ├── BaseResponse.cs
│ │ ├── CatalogBrandEndpoints/
│ │ │ ├── CatalogBrandDto.cs
│ │ │ ├── CatalogBrandListEndpoint.ListCatalogBrandsResponse.cs
│ │ │ └── CatalogBrandListEndpoint.cs
│ │ ├── CatalogItemEndpoints/
│ │ │ ├── CatalogItemDto.cs
│ │ │ ├── CatalogItemGetByIdEndpoint.GetByIdCatalogItemRequest.cs
│ │ │ ├── CatalogItemGetByIdEndpoint.GetByIdCatalogItemResponse.cs
│ │ │ ├── CatalogItemGetByIdEndpoint.cs
│ │ │ ├── CatalogItemListPagedEndpoint.ListPagedCatalogItemRequest.cs
│ │ │ ├── CatalogItemListPagedEndpoint.ListPagedCatalogItemResponse.cs
│ │ │ ├── CatalogItemListPagedEndpoint.cs
│ │ │ ├── CreateCatalogItemEndpoint.CreateCatalogItemRequest.cs
│ │ │ ├── CreateCatalogItemEndpoint.CreateCatalogItemResponse.cs
│ │ │ ├── CreateCatalogItemEndpoint.cs
│ │ │ ├── DeleteCatalogItemEndpoint.DeleteCatalogItemRequest.cs
│ │ │ ├── DeleteCatalogItemEndpoint.DeleteCatalogItemResponse.cs
│ │ │ ├── DeleteCatalogItemEndpoint.cs
│ │ │ ├── UpdateCatalogItemEndpoint.UpdateCatalogItemRequest.cs
│ │ │ ├── UpdateCatalogItemEndpoint.UpdateCatalogItemResponse.cs
│ │ │ └── UpdateCatalogItemEndpoint.cs
│ │ ├── CatalogTypeEndpoints/
│ │ │ ├── CatalogTypeDto.cs
│ │ │ ├── CatalogTypeListEndpoint.ListCatalogTypesResponse.cs
│ │ │ └── CatalogTypeListEndpoint.cs
│ │ ├── CustomSchemaFilters.cs
│ │ ├── Dockerfile
│ │ ├── ImageValidators.cs
│ │ ├── MappingProfile.cs
│ │ ├── Middleware/
│ │ │ └── ExceptionMiddleware.cs
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── launchSettings.json
│ │ ├── PublicApi.csproj
│ │ ├── README.md
│ │ ├── appsettings.Development.json
│ │ ├── appsettings.Docker.json
│ │ └── appsettings.json
│ └── Web/
│ ├── .config/
│ │ └── dotnet-tools.json
│ ├── Areas/
│ │ └── Identity/
│ │ ├── IdentityHostingStartup.cs
│ │ └── Pages/
│ │ ├── Account/
│ │ │ ├── ConfirmEmail.cshtml
│ │ │ ├── ConfirmEmail.cshtml.cs
│ │ │ ├── Login.cshtml
│ │ │ ├── Login.cshtml.cs
│ │ │ ├── Logout.cshtml
│ │ │ ├── Logout.cshtml.cs
│ │ │ ├── Register.cshtml
│ │ │ ├── Register.cshtml.cs
│ │ │ └── _ViewImports.cshtml
│ │ ├── _ValidationScriptsPartial.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── Configuration/
│ │ ├── ConfigureCookieSettings.cs
│ │ ├── ConfigureCoreServices.cs
│ │ ├── ConfigureWebServices.cs
│ │ └── RevokeAuthenticationEvents.cs
│ ├── Constants.cs
│ ├── Controllers/
│ │ ├── Api/
│ │ │ └── BaseApiController.cs
│ │ ├── ManageController.cs
│ │ ├── OrderController.cs
│ │ └── UserController.cs
│ ├── Dockerfile
│ ├── Extensions/
│ │ ├── CacheHelpers.cs
│ │ ├── EmailSenderExtensions.cs
│ │ └── UrlHelperExtensions.cs
│ ├── Features/
│ │ ├── MyOrders/
│ │ │ ├── GetMyOrders.cs
│ │ │ └── GetMyOrdersHandler.cs
│ │ └── OrderDetails/
│ │ ├── GetOrderDetails.cs
│ │ └── GetOrderDetailsHandler.cs
│ ├── HealthChecks/
│ │ ├── ApiHealthCheck.cs
│ │ └── HomePageHealthCheck.cs
│ ├── Interfaces/
│ │ ├── IBasketViewModelService.cs
│ │ ├── ICatalogItemViewModelService.cs
│ │ └── ICatalogViewModelService.cs
│ ├── Pages/
│ │ ├── Admin/
│ │ │ ├── EditCatalogItem.cshtml
│ │ │ ├── EditCatalogItem.cshtml.cs
│ │ │ ├── Index.cshtml
│ │ │ └── Index.cshtml.cs
│ │ ├── Basket/
│ │ │ ├── BasketItemViewModel.cs
│ │ │ ├── BasketViewModel.cs
│ │ │ ├── Checkout.cshtml
│ │ │ ├── Checkout.cshtml.cs
│ │ │ ├── Index.cshtml
│ │ │ ├── Index.cshtml.cs
│ │ │ ├── Success.cshtml
│ │ │ └── Success.cshtml.cs
│ │ ├── Error.cshtml
│ │ ├── Error.cshtml.cs
│ │ ├── FeatureDiagnostics.cshtml
│ │ ├── Index.cshtml
│ │ ├── Index.cshtml.cs
│ │ ├── Privacy.cshtml
│ │ ├── Privacy.cshtml.cs
│ │ ├── SettingsViewModel.cs
│ │ ├── Shared/
│ │ │ ├── Components/
│ │ │ │ └── BasketComponent/
│ │ │ │ ├── Basket.cs
│ │ │ │ └── Default.cshtml
│ │ │ ├── _editCatalog.cshtml
│ │ │ ├── _pagination.cshtml
│ │ │ └── _product.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Services/
│ │ ├── BasketViewModelService.cs
│ │ ├── CachedCatalogViewModelService.cs
│ │ ├── CatalogItemViewModelService.cs
│ │ └── CatalogViewModelService.cs
│ ├── SlugifyParameterTransformer.cs
│ ├── ViewModels/
│ │ ├── Account/
│ │ │ ├── LoginViewModel.cs
│ │ │ ├── LoginWith2faViewModel.cs
│ │ │ ├── RegisterViewModel.cs
│ │ │ └── ResetPasswordViewModel.cs
│ │ ├── BasketComponentViewModel.cs
│ │ ├── CatalogIndexViewModel.cs
│ │ ├── CatalogItemViewModel.cs
│ │ ├── File/
│ │ │ └── FileViewModel.cs
│ │ ├── Manage/
│ │ │ ├── ChangePasswordViewModel.cs
│ │ │ ├── EnableAuthenticatorViewModel.cs
│ │ │ ├── ExternalLoginsViewModel.cs
│ │ │ ├── IndexViewModel.cs
│ │ │ ├── RemoveLoginViewModel.cs
│ │ │ ├── SetPasswordViewModel.cs
│ │ │ ├── ShowRecoveryCodesViewModel.cs
│ │ │ └── TwoFactorAuthenticationViewModel.cs
│ │ ├── OrderDetailViewModel.cs
│ │ ├── OrderItemViewModel.cs
│ │ ├── OrderViewModel.cs
│ │ └── PaginationInfoViewModel.cs
│ ├── Views/
│ │ ├── Account/
│ │ │ ├── Lockout.cshtml
│ │ │ └── LoginWith2fa.cshtml
│ │ ├── Manage/
│ │ │ ├── ChangePassword.cshtml
│ │ │ ├── Disable2fa.cshtml
│ │ │ ├── EnableAuthenticator.cshtml
│ │ │ ├── ExternalLogins.cshtml
│ │ │ ├── GenerateRecoveryCodes.cshtml
│ │ │ ├── ManageNavPages.cs
│ │ │ ├── MyAccount.cshtml
│ │ │ ├── ResetAuthenticator.cshtml
│ │ │ ├── SetPassword.cshtml
│ │ │ ├── ShowRecoverCodes.cshtml
│ │ │ ├── TwoFactorAuthentication.cshtml
│ │ │ ├── _Layout.cshtml
│ │ │ ├── _ManageNav.cshtml
│ │ │ ├── _StatusMessage.cshtml
│ │ │ └── _ViewImports.cshtml
│ │ ├── Order/
│ │ │ ├── Detail.cshtml
│ │ │ └── MyOrders.cshtml
│ │ ├── Shared/
│ │ │ ├── Components/
│ │ │ │ └── Basket/
│ │ │ │ └── Default.cshtml
│ │ │ ├── Error.cshtml
│ │ │ ├── _CookieConsentPartial.cshtml
│ │ │ ├── _Layout.cshtml
│ │ │ ├── _LoginPartial.cshtml
│ │ │ └── _ValidationScriptsPartial.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── Web.csproj
│ ├── appsettings.Development.json
│ ├── appsettings.Docker.json
│ ├── appsettings.json
│ ├── bundleconfig.json
│ ├── compilerconfig.json
│ ├── compilerconfig.json.defaults
│ ├── key-768c1632-cf7b-41a9-bb7a-bff228ae8fba.xml
│ ├── libman.json
│ └── wwwroot/
│ ├── css/
│ │ ├── _variables.css
│ │ ├── _variables.scss
│ │ ├── app.component.css
│ │ ├── app.component.scss
│ │ ├── app.css
│ │ ├── basket/
│ │ │ ├── basket-status/
│ │ │ │ ├── basket-status.component.css
│ │ │ │ └── basket-status.component.scss
│ │ │ ├── basket.component.css
│ │ │ └── basket.component.scss
│ │ ├── catalog/
│ │ │ ├── catalog.component.css
│ │ │ ├── catalog.component.scss
│ │ │ └── pager.css
│ │ ├── orders/
│ │ │ ├── orders.component.css
│ │ │ └── orders.component.scss
│ │ └── shared/
│ │ └── components/
│ │ ├── header/
│ │ │ ├── header.css
│ │ │ └── header.scss
│ │ ├── identity/
│ │ │ ├── identity.css
│ │ │ └── identity.scss
│ │ └── pager/
│ │ ├── pager.css
│ │ └── pager.scss
│ └── js/
│ └── site.js
└── tests/
├── FunctionalTests/
│ ├── FunctionalTests.csproj
│ ├── PublicApi/
│ │ ├── ApiTestFixture.cs
│ │ ├── ApiTokenHelper.cs
│ │ └── AuthEndpoints/
│ │ └── AuthenticateEndpoint.cs
│ └── Web/
│ ├── Controllers/
│ │ ├── AccountControllerSignIn.cs
│ │ ├── CatalogControllerIndex.cs
│ │ └── OrderControllerIndex.cs
│ ├── Pages/
│ │ ├── Basket/
│ │ │ ├── BasketPageCheckout.cs
│ │ │ ├── CheckoutTest.cs
│ │ │ └── IndexTest.cs
│ │ └── HomePageOnGet.cs
│ ├── WebPageHelpers.cs
│ └── WebTestFixture.cs
├── IntegrationTests/
│ ├── IntegrationTests.csproj
│ └── Repositories/
│ ├── BasketRepositoryTests/
│ │ └── SetQuantities.cs
│ └── OrderRepositoryTests/
│ ├── GetById.cs
│ └── GetByIdWithItemsAsync.cs
├── PublicApiIntegrationTests/
│ ├── ApiTokenHelper.cs
│ ├── AuthEndpoints/
│ │ └── AuthenticateEndpointTest.cs
│ ├── CatalogItemEndpoints/
│ │ ├── CatalogItemGetByIdEndpointTest.cs
│ │ ├── CatalogItemListPagedEndpoint.cs
│ │ ├── CreateCatalogItemEndpointTest.cs
│ │ └── DeleteCatalogItemEndpointTest.cs
│ ├── ProgramTest.cs
│ ├── PublicApiIntegrationTests.csproj
│ └── appsettings.test.json
└── UnitTests/
├── ApplicationCore/
│ ├── Entities/
│ │ ├── BasketTests/
│ │ │ ├── BasketAddItem.cs
│ │ │ ├── BasketRemoveEmptyItems.cs
│ │ │ └── BasketTotalItems.cs
│ │ └── OrderTests/
│ │ └── OrderTotal.cs
│ ├── Extensions/
│ │ ├── JsonExtensions.cs
│ │ ├── TestChild.cs
│ │ └── TestParent.cs
│ ├── Services/
│ │ └── BasketServiceTests/
│ │ ├── AddItemToBasket.cs
│ │ ├── DeleteBasket.cs
│ │ └── TransferBasket.cs
│ └── Specifications/
│ ├── BasketWithItemsSpecification.cs
│ ├── CatalogFilterPaginatedSpecification.cs
│ ├── CatalogFilterSpecification.cs
│ ├── CatalogItemsSpecification.cs
│ └── CustomerOrdersWithItemsSpecification.cs
├── Builders/
│ ├── AddressBuilder.cs
│ ├── BasketBuilder.cs
│ └── OrderBuilder.cs
├── MediatorHandlers/
│ └── OrdersTests/
│ ├── GetMyOrders.cs
│ └── GetOrderDetails.cs
├── UnitTests.csproj
└── Web/
└── Extensions/
└── CacheHelpersTests/
├── GenerateBrandsCacheKey.cs
├── GenerateCatalogItemCacheKey.cs
└── GenerateTypesCacheKey.cs
================================================
FILE CONTENTS
================================================
================================================
FILE: .ado/eshoponweb-cd-aci.yml
================================================
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
# trigger: none
resources:
pipelines:
- pipeline: eshoponweb-ci-dockercompose
source: eshoponweb-ci-dockercompose # given pipeline name
trigger: true
repositories:
- repository: self
trigger: none
variables:
location: 'centralus'
templateFile: 'infra/aci.bicep'
subscriptionid: 'YOUR-SUBSCRIPTION-ID'
azureserviceconnection: 'azure subs'
webappname: 'az400eshop-NAME'
acr-login-server: 'YOUR-ACR.azurecr.io'
acr-username: 'ACR-USERNAME'
resource-group: 'AZ400-EWebShop-NAME'
stages:
- stage: Deploy
displayName: Docker Compose to ACI
#variable group referencing KV secret
variables:
- group: 'eshopweb-vg'
jobs:
- job: Deploy
pool:
vmImage: ubuntu-latest
steps:
# Deploy Azure Container Instance using Bicep
- task: AzureResourceManagerTemplateDeployment@3
displayName: Deploy ACI Bicep
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: '$(azureserviceconnection)'
subscriptionId: '$(subscriptionid)'
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resource-group)'
location: '$(location)'
templateLocation: 'Linked artifact'
csmFile: '$(templateFile)'
overrideParameters: ' -name $(webappname) -image $(acr-login-server)/eshopwebmvc:latest -server $(acr-login-server) -username $(acr-username) -password $(acr-secret)'
deploymentMode: 'Incremental'
# deploymentOutputs: 'asp-json'
================================================
FILE: .ado/eshoponweb-cd-webapp-code.yml
================================================
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
# Trigger CD when CI executed succesfully
resources:
pipelines:
- pipeline: eshoponweb-ci
source: eshoponweb-ci # given pipeline name
trigger: true
variables:
resource-group: 'AZ400-EWebShop-NAME'
location: 'centralus'
templateFile: 'webapp.bicep'
subscriptionid: 'YOUR-SUBSCRIPTION-ID'
azureserviceconnection: 'azure subs'
webappname: 'az400-webapp-NAME'
# webappname: 'webapp-windows-eshop'
stages:
- stage: Deploy
displayName: Deploy to WebApp
jobs:
- job: Deploy
pool:
vmImage: windows-latest
steps:
#download artifacts
- download: eshoponweb-ci
# Deploy App Service Plan + App Service using Bicep
- task: AzureResourceManagerTemplateDeployment@3
displayName: Deploy App Service Plan Bicep
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: '$(azureserviceconnection)'
subscriptionId: '$(subscriptionid)'
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resource-group)'
location: '$(location)'
templateLocation: 'Linked artifact'
csmFile: '$(Pipeline.Workspace)/eshoponweb-ci/Bicep/$(templateFile)'
overrideParameters: '-webAppName $(webappname) -location $(location)'
deploymentMode: 'Incremental'
deploymentOutputs: 'asp-json'
#Publish Website to Azure WebApp
- task: AzureRmWebAppDeployment@4
displayName: Publish Website to WebApp
inputs:
ConnectionType: 'AzureRM'
azureSubscription: '$(azureserviceconnection)'
appType: 'webApp'
WebAppName: '$(webappname)'
packageForLinux: '$(Pipeline.Workspace)/eshoponweb-ci/Website/Web.zip'
================================================
FILE: .ado/eshoponweb-cd-webapp-docker.yml
================================================
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
resources:
repositories:
- repository: self
trigger: none
variables:
azureServiceConnection: 'azure subs'
resourceGroup: 'rg-az400-container-NAME'
location: 'centralus'
subscriptionId: 'YOUR-SUBSCRIPTION-ID'
stages:
- stage: Deploy
jobs:
- job: Deploy
pool:
vmImage: ubuntu-latest
steps:
- task: AzureResourceManagerTemplateDeployment@3
displayName: Deploy App Service using Bicep
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: $(azureServiceConnection)
subscriptionId: $(subscriptionId)
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resourceGroup)'
location: '$(location)'
templateLocation: 'Linked artifact'
csmFile: 'infra/webapp-docker.bicep'
deploymentMode: 'Incremental'
- task: AzureResourceManagerTemplateDeployment@3
displayName: Add Role Assignment using Bicep
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: $(azureServiceConnection)
subscriptionId: $(subscriptionId)
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resourceGroup)'
location: '$(location)'
templateLocation: 'Linked artifact'
csmFile: 'infra/webapp-to-acr-roleassignment.bicep'
deploymentMode: 'Incremental'
================================================
FILE: .ado/eshoponweb-cd-webapp-dockercompose.yml
================================================
#NOT WORKING YET
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
#locally docker compose works, deploying to Azure WebApp is succesful but something still fails
# trigger: none
resources:
repositories:
- repository: self
trigger: none
variables:
tag: '$(Build.BuildId)'
resource-group: 'AZ400-EWebShop-NAME'
location: 'centralus'
templateFile: '.ado/IaC/app-plan.bicep'
subscriptionid: 'YOUR-SUBSCRIPTION-ID'
azureserviceconnection: 'azure subs'
webappname: 'az400-webapp-compose-NAME'
composeFile: 'docker-compose-webapp.yml'
acr-login-server: 'YOUR-ACR.azurecr.io'
acr-username: 'YOUR-ACR'
stages:
- stage: Deploy
displayName: Docker Compose to WebApp
jobs:
- job: Deploy
pool:
vmImage: ubuntu-latest
steps:
# Deploy App Service Plan using Bicep
- task: AzureResourceManagerTemplateDeployment@3
displayName: Deploy App Service Plan Bicep
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: $(azureserviceconnection)
subscriptionId: $(subscriptionid)
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resource-group)'
location: '$(location)'
templateLocation: 'Linked artifact'
csmFile: '$(templateFile)'
deploymentMode: 'Incremental'
deploymentOutputs: 'asp-json'
#Parse App Service Plan variable
- task: PowerShell@2
displayName: Parse Bicep Output
inputs:
targetType: 'inline'
script: |
$var=ConvertFrom-Json '$(asp-json)'
$value=$var.asplan.value
Write-Host "##vso[task.setvariable variable=appserviceplan;]$value"
echo $appserviceplan
#Replace tokens in Docker Compose file
- task: qetza.replacetokens.replacetokens-task.replacetokens@3
inputs:
targetFiles: 'docker-compose-webapp.yml'
encoding: 'auto'
writeBOM: true
actionOnMissing: 'warn'
keepToken: false
actionOnNoFiles: 'continue'
enableTransforms: false
tokenPrefix: '__'
tokenSuffix: '__'
enableRecursion: false
useLegacyPattern: false
enableTelemetry: true
#Docker Login into private ACR (TODO alternative, using Docker task and docker ADO Service Connection)
#Deploy using Docker Compose
- task: AzureCLI@2
inputs:
azureSubscription: '$(azureserviceconnection)'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az webapp create --resource-group $(resource-group) --plan $(appserviceplan) --name $(webappname) --multicontainer-config-type compose --multicontainer-config-file $(composeFile)
az webapp config container set --resource-group $(resource-group) --name $(webappname) --docker-registry-server-user $(acr-username) --docker-registry-server-password $(acr-secret) --docker-registry-server-url https://$(acr-login-server)
================================================
FILE: .ado/eshoponweb-cd-windows-cm.yml
================================================
variables:
resource-group: 'AZ400-EWebShop-NAME'
location: 'centralus'
templateFile: 'infra/simple-windows-vm.bicep'
azureserviceconnection: 'azure subs'
stages:
- stage: Deploy
displayName: Deploy the Bicep template
jobs:
- job: Deploy
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
- task: AzureCLI@2
inputs:
azureSubscription: $(azureserviceconnection)
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az group create -l $(location) -n $(resource-group)
az deployment group create -f $(templateFile) -g $(resource-group)
================================================
FILE: .ado/eshoponweb-ci-docker.yml
================================================
# NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
resources:
repositories:
- repository: self
trigger: none
variables:
azureServiceConnection: 'azure subs'
subscriptionId: 'YOUR-SUBSCRIPTION-ID'
resourceGroup: 'rg-az400-container-NAME'
location: 'centralus'
stages:
- stage: Build
jobs:
- job: Build
pool:
vmImage: 'ubuntu-latest'
steps:
- task: AzureResourceManagerTemplateDeployment@3
displayName: Deploy ACR using Bicep
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: $(azureServiceConnection)
subscriptionId: $(subscriptionId)
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resourceGroup)'
location: '$(location)'
templateLocation: 'Linked artifact'
csmFile: 'infra/acr.bicep'
deploymentMode: 'Incremental'
deploymentOutputs: 'outputJson'
- task: PowerShell@2
displayName: Parse Bicep Output
inputs:
targetType: 'inline'
script: |
$var=ConvertFrom-Json '$(outputJson)'
$value=$var.acrLoginServer.value
Write-Host "##vso[task.setvariable variable=acrLoginServer;]$value"
- task: Docker@0
displayName: 'Build the docker image'
inputs:
azureSubscription: $(azureServiceConnection)
azureContainerRegistry: $(acrLoginServer)
dockerFile: 'src/Web/Dockerfile'
defaultContext: false
context: $(Build.SourcesDirectory)
includeLatestTag: true
imageName: eshoponweb/web:$(Build.BuildId)
- task: Docker@0
displayName: 'Push the docker images'
inputs:
azureSubscription: $(azureServiceConnection)
azureContainerRegistry: $(acrLoginServer)
action: 'Push an image'
imageName: eshoponweb/web:$(Build.BuildId)
includeLatestTag: true
================================================
FILE: .ado/eshoponweb-ci-dockercompose.yml
================================================
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
# Docker
# Build a Docker image
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
# trigger:
# - main
resources:
repositories:
- repository: self
trigger: none
variables:
tag: '$(Build.BuildId)'
resource-group: 'AZ400-EWebShop-NAME'
location: 'centralus'
templateFile: 'infra/acr.bicep'
azureserviceconnection: 'azure subs'
subscriptionId: 'YOUR-SUBSCRIPTION-ID'
stages:
- stage: Build
displayName: Create ACR for images
jobs:
- job: Build
pool:
vmImage: ubuntu-latest
steps:
#Create ACR to keep docker images
- task: AzureResourceManagerTemplateDeployment@3
displayName: Deploy ACR Bicep
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: $(azureserviceconnection)
subscriptionId: $(subscriptionId)
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resource-group)'
location: '$(location)'
templateLocation: 'Linked artifact'
csmFile: '$(templateFile)'
deploymentMode: 'Incremental'
deploymentOutputs: 'acr-json'
#Parse ACR login server variable
- task: PowerShell@2
displayName: Parse Bicep Output
inputs:
targetType: 'inline'
script: |
$var=ConvertFrom-Json '$(acr-json)'
$value=$var.acrloginServer.value
Write-Host "##vso[task.setvariable variable=acrloginserver;]$value"
echo $acrloginserver
# docker compose build images
- task: DockerCompose@1
displayName: Build Docker Compose
inputs:
containerregistrytype: 'Azure Container Registry'
azureSubscription: '$(azureserviceconnection)'
azureContainerRegistry: '$(acrloginserver)'
dockerComposeFile: '**/docker-compose.yml'
projectName:
action: 'Build services'
additionalImageTags: '$(Build.BuildNumber)'
includeLatestTag: true
- task: DockerCompose@1
displayName: Push Docker Compose
inputs:
containerregistrytype: 'Azure Container Registry'
azureSubscription: $(azureserviceconnection)
azureContainerRegistry: '$(acrloginserver)'
dockerComposeFile: '**/docker-compose.yml'
projectName:
action: 'Push services'
additionalImageTags: '$(Build.BuildNumber)'
includeLatestTag: true
================================================
FILE: .ado/eshoponweb-ci-mend.yml
================================================
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
# trigger:
# - main
resources:
repositories:
- repository: self
trigger: none
stages:
- stage: Build
displayName: Build .Net Core Solution
jobs:
- job: Build
pool:
vmImage: ubuntu-latest
steps:
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: 'restore'
projects: '**/*.sln'
feedsToUse: 'select'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: 'build'
projects: '**/*.sln'
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: 'test'
projects: 'tests/UnitTests/*.csproj'
- task: WhiteSource@21
inputs:
cwd: '$(System.DefaultWorkingDirectory)'
projectName: 'eShopOnWeb'
- task: DotNetCoreCLI@2
displayName: Publish
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-o $(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
displayName: Publish Artifacts ADO - Website
inputs:
pathToPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: Website
================================================
FILE: .ado/eshoponweb-ci-pr.yml
================================================
resources:
repositories:
- repository: self
trigger: none
stages:
- stage: Build
displayName: Build .Net Core Solution
jobs:
- job: Build
pool:
vmImage: ubuntu-latest
steps:
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: 'restore'
projects: '**/*.sln'
feedsToUse: 'select'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: 'build'
projects: '**/*.sln'
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: 'test'
projects: 'tests/UnitTests/*.csproj'
- task: DotNetCoreCLI@2
displayName: Publish
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-o $(Build.ArtifactStagingDirectory)'
================================================
FILE: .ado/eshoponweb-ci.yml
================================================
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
# trigger:
# - main
resources:
repositories:
- repository: self
trigger: none
stages:
- stage: Build
displayName: Build .Net Core Solution
jobs:
- job: Build
pool:
vmImage: ubuntu-latest
steps:
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: 'restore'
projects: '**/*.sln'
feedsToUse: 'select'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: 'build'
projects: '**/*.sln'
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: 'test'
projects: 'tests/UnitTests/*.csproj'
- task: DotNetCoreCLI@2
displayName: Publish
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-o $(Build.ArtifactStagingDirectory)'
- task: PublishPipelineArtifact@1
displayName: Publish Artifacts ADO - Website
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
artifact: 'Website'
publishLocation: 'pipeline'
- task: PublishPipelineArtifact@1
displayName: Publish Artifacts ADO - Bicep
inputs:
targetPath: '$(Build.SourcesDirectory)/infra/webapp.bicep'
artifact: 'Bicep'
publishLocation: 'pipeline'
================================================
FILE: .ado/eshoponweb-sonar-ci.yml
================================================
#NAME THE PIPELINE SAME AS FILE (WITHOUT ".yml")
# trigger:
# - main
resources:
repositories:
- repository: self
trigger: none
stages:
- stage: Build
displayName: Build .Net Core Solution
jobs:
- job: Build
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
fetchDepth: 0
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: 'restore'
projects: '**/*.sln'
feedsToUse: 'select'
- task: SonarCloudPrepare@1
inputs:
SonarCloud: 'SonarSC'
organization: 'Your Sonarcloud org'
scannerMode: 'MSBuild'
projectKey: 'your sonarcloud project key'
projectName: 'your sonarcloud project name'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: 'build'
projects: '**/*.sln'
- task: DotNetCoreCLI@2
displayName: Test
inputs:
command: 'test'
projects: 'tests/UnitTests/*.csproj'
- task: SonarCloudAnalyze@1
- task: SonarCloudPublish@1
inputs:
pollingTimeoutSec: '300'
- task: DotNetCoreCLI@2
displayName: Publish
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-o $(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
displayName: Publish Artifacts ADO - Website
inputs:
pathToPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: Website
================================================
FILE: .dockerignore
================================================
.dockerignore
.env
.git
.gitignore
.vs
.vscode
*/bin
*/obj
**/.toolstarget
================================================
FILE: .editorconfig
================================================
###############################
# 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
# Instance fields are camelCase and start with _
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
dotnet_naming_symbols.instance_fields.applicable_kinds = field
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _
###############################
# 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###############################
######################################
# Configure Nullable Reference Types #
######################################
[{**/*Dto.cs,**/*Request.cs,**/*Response.cs}]
# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = none
================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
================================================
FILE: .github/workflows/dotnetcore.yml
================================================
name: eShopOnWeb Build and Test
#Triggers (uncomment line below to use it)
#on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: "8.0.x"
dotnet-quality: "preview"
- name: Build with dotnet
run: dotnet build ./eShopOnWeb.sln --configuration Release
- name: Test with dotnet
run: dotnet test ./eShopOnWeb.sln --configuration Release
================================================
FILE: .github/workflows/eshoponweb-cicd.yml
================================================
name: eShopOnWeb Build and Test
#Triggers (uncomment line below to use it)
#on: [push, workflow_dispatch]
#Environment variables https://docs.github.com/en/actions/learn-github-actions/environment-variables
env:
RESOURCE-GROUP: rg-eshoponweb-NAME
LOCATION: westeurope
TEMPLATE-FILE: infra/webapp.bicep
SUBSCRIPTION-ID: YOUR-SUBS-ID
WEBAPP-NAME: eshoponweb-webapp-NAME
jobs:
#Build, test and publish .net web project in repository
buildandtest:
runs-on: ubuntu-latest
steps:
#checkout the repository
- uses: actions/checkout@v5
#prepare runner for desired .net version SDK
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: "8.0.x"
dotnet-quality: "preview"
#Build/Test/Publish the .net project
- name: Build with dotnet
run: dotnet build ./eShopOnWeb.sln --configuration Release
- name: Test with dotnet
run: dotnet test ./eShopOnWeb.sln --configuration Release
- name: dotnet publish
run: |
dotnet publish ./src/Web/Web.csproj -c Release -o ${{env.DOTNET_ROOT}}/myapp
cd ${{env.DOTNET_ROOT}}/myapp
zip -r ../app.zip .
# upload the published website code artifacts
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v5
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/app.zip
# upload the bicep template as artifacts for next job
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v5
with:
name: bicep-template
path: ${{ env.TEMPLATE-FILE }}
# Use Bicep to deploy infrastructure + Publish webapp
deploy:
runs-on: ubuntu-latest
needs: buildandtest
environment:
name: "Development"
steps:
#Download the publish files created in previous job
- name: Download artifact from build job
uses: actions/download-artifact@v6
with:
name: .net-app
path: .net-app
#Download the bicep templates from previous job
- name: Download artifact from build job
uses: actions/download-artifact@v6
with:
name: bicep-template
path: bicep-template
#Login in your azure subscription using a service principal (credentials stored as GitHub Secret in repo)
- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
# Deploy Azure WebApp using Bicep file
- name: deploy
uses: azure/arm-deploy@v2
with:
subscriptionId: ${{ env.SUBSCRIPTION-ID }}
resourceGroupName: ${{ env.RESOURCE-GROUP }}
template: bicep-template/webapp.bicep
parameters: "webAppName=${{ env.WEBAPP-NAME }} location=${{ env.LOCATION }}"
failOnStdErr: false
# Publish website to Azure App Service (WebApp)
# Step disabled due to issue where the site sometimes can't be found: https://github.com/microsoft/pipelines-appservice-lib/issues/56. Instead deploy using CLI
- name: Publish Website to WebApp
if: false #Disable step due to comment above
uses: Azure/webapps-deploy@v3
with:
type: ZIP
app-name: ${{ env.WEBAPP-NAME }}
package: .net-app/app.zip
# Publish website to Azure App Service using CLI (WebApp)
- name: Publish Website to WebApp
uses: Azure/cli@v2
with:
inlineScript: |
az webapp deploy --name ${{ env.WEBAPP-NAME }} --resource-group ${{ env.RESOURCE-GROUP }} --src-path .net-app/app.zip --type zip
================================================
FILE: .github/workflows/richnav.yml
================================================
name: eShopOnWeb - Code Index
on: workflow_dispatch
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v5
- name: Setup .NET Core
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.x
- name: Build with dotnet
run: dotnet build ./Everything.sln --configuration Release /bl
- uses: microsoft/RichCodeNavIndexer@v0.1
with:
repo-token: ${{ github.token }}
languages: "csharp"
environment: "internal"
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
**/wwwroot/lib/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
pub/
/src/Web/WebMVC/Properties/PublishProfiles/eShopOnContainersWebMVC2016 - Web Deploy-publish.ps1
/src/Web/WebMVC/Properties/PublishProfiles/publish-module.psm1
/src/Services/Identity/eShopOnContainers.Identity/Properties/launchSettings.json
#Ignore marker-file used to know which docker files we have.
.eshopdocker_*
.devcontainer
.azure
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"ms-dotnettools.csharp",
"formulahendry.dotnet-test-explorer",
"ms-vscode.vscode-node-azure-pack",
"ms-kubernetes-tools.vscode-kubernetes-tools",
"redhat.vscode-yaml",
"ms-azuretools.azure-dev"
]
}
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/Web/bin/Debug/net8.0/Web.dll",
"args": [],
"cwd": "${workspaceFolder}/src/Web",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "^\\s*Now listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
================================================
FILE: .vscode/tasks.json
================================================
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/Web/Web.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/src/Web/Web.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/src/Web/Web.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}
================================================
FILE: CodeCoverage.runsettings
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- File name extension must be .runsettings -->
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Configuration>
<CodeCoverage>
<!--
Additional paths to search for .pdb (symbol) files. Symbols must be found for modules to be instrumented.
If .pdb files are in the same folder as the .dll or .exe files, they are automatically found. Otherwise, specify them here.
Note that searching for symbols increases code coverage runtime. So keep this small and local.
-->
<!--
<SymbolSearchPaths>
<Path>C:\Users\User\Documents\Visual Studio 2012\Projects\ProjectX\bin\Debug</Path>
<Path>\\mybuildshare\builds\ProjectX</Path>
</SymbolSearchPaths>
-->
<!--
About include/exclude lists:
Empty "Include" clauses imply all; empty "Exclude" clauses imply none.
Each element in the list is a regular expression (ECMAScript syntax). See https://docs.microsoft.com/visualstudio/ide/using-regular-expressions-in-visual-studio.
An item must first match at least one entry in the include list to be included.
Included items must then not match any entries in the exclude list to remain included.
-->
<!-- Match assembly file paths: -->
<ModulePaths>
<Exclude>
<ModulePath>.*guardclauses\.dll$</ModulePath>
<ModulePath>.*moq\.dll$</ModulePath>
<ModulePath>.*unittests\.dll$</ModulePath>
</Exclude>
</ModulePaths>
<!-- Match fully qualified names of functions: -->
<!-- (Use "\." to delimit namespaces in C# or Visual Basic, "::" in C++.)
<Functions>
<Exclude>
<Function>^Fabrikam\.UnitTest\..*</Function>
<Function>^std::.*</Function>
<Function>^ATL::.*</Function>
<Function>.*::__GetTestMethodInfo.*</Function>
<Function>^Microsoft::VisualStudio::CppCodeCoverageFramework::.*</Function>
<Function>^Microsoft::VisualStudio::CppUnitTestFramework::.*</Function>
</Exclude>
</Functions> -->
<!-- Match attributes on any code element: -->
<Attributes>
<Exclude>
<!-- Don't forget "Attribute" at the end of the name
<Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>
<Attribute>^System\.Diagnostics\.DebuggerNonUserCodeAttribute$</Attribute>
<Attribute>^System\.Runtime\.CompilerServices.CompilerGeneratedAttribute$</Attribute>
<Attribute>^System\.CodeDom\.Compiler.GeneratedCodeAttribute$</Attribute>
<Attribute>^System\.Diagnostics\.CodeAnalysis.ExcludeFromCodeCoverageAttribute$</Attribute>-->
</Exclude>
</Attributes>
<!-- Match the path of the source files in which each method is defined:
<Sources>
<Exclude>
<Source>.*\\atlmfc\\.*</Source>
<Source>.*\\vctools\\.*</Source>
<Source>.*\\public\\sdk\\.*</Source>
<Source>.*\\microsoft sdks\\.*</Source>
<Source>.*\\vc\\include\\.*</Source>
</Exclude>
</Sources> -->
<!-- Match the company name property in the assembly: -->
<CompanyNames>
<Exclude>
<CompanyName>.*microsoft.*</CompanyName>
</Exclude>
</CompanyNames>
<!-- Match the public key token of a signed assembly: -->
<PublicKeyTokens>
<!-- Exclude Visual Studio extensions:
<Exclude>
<PublicKeyToken>^B77A5C561934E089$</PublicKeyToken>
<PublicKeyToken>^B03F5F7F11D50A3A$</PublicKeyToken>
<PublicKeyToken>^31BF3856AD364E35$</PublicKeyToken>
<PublicKeyToken>^89845DCD8080CC91$</PublicKeyToken>
<PublicKeyToken>^71E9BCE111E9429C$</PublicKeyToken>
<PublicKeyToken>^8F50407C4E9E73B6$</PublicKeyToken>
<PublicKeyToken>^E361AF139669C375$</PublicKeyToken>
</Exclude>-->
</PublicKeyTokens>
<!-- We recommend you do not change the following values: -->
<UseVerifiableInstrumentation>True</UseVerifiableInstrumentation>
<AllowLowIntegrityProcesses>True</AllowLowIntegrityProcesses>
<CollectFromChildProcesses>True</CollectFromChildProcesses>
<CollectAspDotNet>False</CollectAspDotNet>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
================================================
FILE: Directory.Packages.props
================================================
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<TargetFramework>net8.0</TargetFramework>
<AspNetVersion>8.0.0</AspNetVersion>
<SystemExtensionVersion>8.0.1</SystemExtensionVersion>
<EntityFramworkCoreVersion>8.0.0</EntityFramworkCoreVersion>
<VSCodeGeneratorVersion>9.0.0</VSCodeGeneratorVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.ApiEndpoints" Version="4.1.0" />
<PackageVersion Include="Ardalis.GuardClauses" Version="5.0.0" />
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="9.3.1" />
<PackageVersion Include="Ardalis.Result" Version="10.1.0" />
<PackageVersion Include="Ardalis.Specification" Version="9.3.1" />
<PackageVersion Include="Ardalis.ListStartupServices" Version="1.1.4" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.4.0" />
<PackageVersion Include="Azure.Identity" Version="1.16.0" />
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageVersion Include="BlazorInputFile" Version="0.2.0" />
<PackageVersion Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageVersion Include="BuildBundlerMinifier" Version="3.2.449" PrivateAssets="All" />
<PackageVersion Include="FluentValidation" Version="11.10.0" />
<PackageVersion Include="MediatR" Version="12.5.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.13" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.15" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.15" />
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.12" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="8.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="$(SystemExtensionVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="$(VSCodeGeneratorVersion)" />
<PackageVersion Include="Microsoft.Web.LibraryManager.Build" Version="3.0.71" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageVersion Include="Microsoft.FeatureManagement.AspNetCore" Version="4.0.0" />
<PackageVersion Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="8.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageVersion Include="MinimalApi.Endpoint" Version="1.3.0" />
<PackageVersion Include="Moq" Version="4.20.70" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
<PackageVersion Include="System.Net.Http.Json" Version="$(SystemExtensionVersion)" />
<PackageVersion Include="System.Security.Claims" Version="4.3.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.8.1" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.1.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="7.3.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="7.2.0" />
<!-- Test -->
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit.runner.console" Version="2.9.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
<PackageVersion Include="MSTest.TestAdapter" Version="3.5.2" />
<PackageVersion Include="MSTest.TestFramework" Version="3.6.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
</ItemGroup>
</Project>
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) .NET Foundation and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: MTT-Notes.md
================================================
# App modified
For architecture simplicity (and problems related to deploy SQL on free subscriptions), inmemory database option is selected.
By default, the project uses a real database. If you want an in memory database, /src/Infrastructure/Dependencies.cs line 13:
```
var useOnlyInMemoryDatabase = true;
```
# ADO Pipelines
Located under the folder ".ado", you can find the following YAML pipelines:
- **main-ci.yml** : Dotnet CI pipeline. Build + Test + Publish. Upload artifacts for website and webapp bicep file.
- **main-ci-containers-compose.yml** : It first creates an ACR and build/push docker containers based on docker compose. WARNING: you need to have an existing RG.
- **main-cd-web-aci.yml** : deploys container image on ACI using Bicep template. WARNING: you need to provide ACR username and password (using variable group / key vault).Contributor role is for management plane operations to manage key vaults. It does not allow access to keys, secrets and certificates. **READ/LIST access needed for used Service Principal in ADO VG**. TODO: using managed identity.
- **main-cd-web-webapp.yml** : triggered by **main-ci.yml** and deploys app artifacts created by **main-ci.yml** to Azure Web App (linux). It uses bicep to create App Service Plan and WebApp, and publishes code to the webapp.
# Deployment options
## Azure App Service single container for only WEB (TODO)
Deployment of the Web solution works with the following settings:

================================================
FILE: README.md
================================================
[](https://github.com/dotnet-architecture/eShopOnWeb/actions)
# Microsoft eShopOnWeb ASP.NET Core Reference Application
Sample ASP.NET Core reference application, powered by Microsoft, demonstrating a single-process (monolithic) application architecture and deployment model. If you're new to .NET development, read the [Getting Started for Beginners](https://github.com/dotnet-architecture/eShopOnWeb/wiki/Getting-Started-for-Beginners) guide.
A list of Frequently Asked Questions about this repository can be found [here](https://github.com/dotnet-architecture/eShopOnWeb/wiki/Frequently-Asked-Questions).
## Overview Video
[Steve "ardalis" Smith](https://twitter.com/ardalis) recorded [a live stream providing an overview of the eShopOnWeb reference app](https://www.youtube.com/watch?v=vRZ8ucGac8M&ab_channel=Ardalis) in October 2020.
## eBook
This reference application is meant to support the free .PDF download ebook: [Architecting Modern Web Applications with ASP.NET Core and Azure](https://aka.ms/webappebook), updated to **ASP.NET Core 8.0**. [Also available in ePub/mobi formats](https://dotnet.microsoft.com/learn/web/aspnet-architecture).
You can also read the book in online pages at the .NET docs here:
https://docs.microsoft.com/dotnet/architecture/modern-web-apps-azure/
[<img src="https://dotnet.microsoft.com/blob-assets/images/e-books/aspnet.png" height="300" />](https://dotnet.microsoft.com/learn/web/aspnet-architecture)
The **eShopOnWeb** sample is related to the [eShopOnContainers](https://github.com/dotnet/eShopOnContainers) sample application which, in that case, focuses on a microservices/containers-based application architecture. However, **eShopOnWeb** is much simpler in regards to its current functionality and focuses on traditional Web Application Development with a single deployment.
The goal for this sample is to demonstrate some of the principles and patterns described in the [eBook](https://aka.ms/webappebook). It is not meant to be an eCommerce reference application, and as such it does not implement many features that would be obvious and/or essential to a real eCommerce application.
> ### VERSIONS
> #### The `main` branch is currently running ASP.NET Core 8.0.
> #### Older versions are tagged.
## Topics (eBook TOC)
- Introduction
- Characteristics of Modern Web Applications
- Choosing Between Traditional Web Apps and SPAs
- Architectural Principles
- Common Web Application Architectures
- Common Client Side Technologies
- Developing ASP.NET Core MVC Apps
- Working with Data in ASP.NET Core Apps
- Testing ASP.NET Core MVC Apps
- Development Process for Azure-Hosted ASP.NET Core Apps
- Azure Hosting Recommendations for ASP.NET Core Web Apps
## Running the sample using Azd template
The store's home page should look like this:

The Azure Developer CLI (`azd`) is a developer-centric command-line interface (CLI) tool for creating Azure applications.
You need to install it before running and deploying with Azure Developer CLI.
### Windows
```powershell
powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression"
```
### Linux/MacOS
```
curl -fsSL https://aka.ms/install-azd.sh | bash
```
And you can also install with package managers, like winget, choco, and brew. For more details, you can follow the documentation: https://aka.ms/azure-dev/install.
After logging in with the following command, you will be able to use the azd cli to quickly provision and deploy the application.
```
azd auth login
```
Then, execute the `azd init` command to initialize the environment.
```
azd init -t dotnet-architecture/eShopOnWeb
```
Run `azd up` to provision all the resources to Azure and deploy the code to those resources.
```
azd up
```
According to the prompt, enter an `env name`, and select `subscription` and `location`, these are the necessary parameters when you create resources. Wait a moment for the resource deployment to complete, click the web endpoint and you will see the home page.
**Notes:**
1. Considering security, we store its related data (id, password) in the **Azure Key Vault** when we create the database, and obtain it from the Key Vault when we use it. This is different from directly deploying applications locally.
2. The resource group name created in azure portal will be **rg-{env name}**.
You can also run the sample directly locally (See below).
## Running the sample locally
Most of the site's functionality works with just the web application running. However, the site's Admin page relies on Blazor WebAssembly running in the browser, and it must communicate with the server using the site's PublicApi web application. You'll need to also run this project. You can configure Visual Studio to start multiple projects, or just go to the PublicApi folder in a terminal window and run `dotnet run` from there. After that from the Web folder you should run `dotnet run --launch-profile Web`. Now you should be able to browse to `https://localhost:5001/`. The admin part in Blazor is accessible to `https://localhost:5001/admin`
Note that if you use this approach, you'll need to stop the application manually in order to build the solution (otherwise you'll get file locking errors).
After cloning or downloading the sample you must setup your database.
To use the sample with a persistent database, you will need to run its Entity Framework Core migrations before you will be able to run the app.
You can also run the samples in Docker (see below).
### Configuring the sample to use SQL Server
1. By default, the project uses a real database. If you want an in memory database, you can add in the `appsettings.json` file in the Web folder
```json
{
"UseOnlyInMemoryDatabase": true
}
```
1. Ensure your connection strings in `appsettings.json` point to a local SQL Server instance.
1. Ensure the tool EF was already installed. You can find some help [here](https://docs.microsoft.com/ef/core/miscellaneous/cli/dotnet)
```
dotnet tool update --global dotnet-ef
```
1. Open a command prompt in the Web folder and execute the following commands:
```
dotnet restore
dotnet tool restore
dotnet ef database update -c catalogcontext -p ../Infrastructure/Infrastructure.csproj -s Web.csproj
dotnet ef database update -c appidentitydbcontext -p ../Infrastructure/Infrastructure.csproj -s Web.csproj
```
These commands will create two separate databases, one for the store's catalog data and shopping cart information, and one for the app's user credentials and identity data.
1. Run the application.
The first time you run the application, it will seed both databases with data such that you should see products in the store, and you should be able to log in using the demouser@microsoft.com account.
Note: If you need to create migrations, you can use these commands:
```
-- create migration (from Web folder CLI)
dotnet ef migrations add InitialModel --context catalogcontext -p ../Infrastructure/Infrastructure.csproj -s Web.csproj -o Data/Migrations
dotnet ef migrations add InitialIdentityModel --context appidentitydbcontext -p ../Infrastructure/Infrastructure.csproj -s Web.csproj -o Identity/Migrations
```
## Running the sample in the dev container
This project includes a `.devcontainer` folder with a [dev container configuration](https://containers.dev/), which lets you use a container as a full-featured dev environment.
You can use the dev container to build and run the app without needing to install any of its tools locally! You can work in GitHub Codespaces or the VS Code Dev Containers extension.
Learn more about using the dev container in its [readme](/.devcontainer/devcontainerreadme.md).
## Running the sample using Docker
You can run the Web sample by running these commands from the root folder (where the .sln file is located):
```
docker-compose build
docker-compose up
```
You should be able to make requests to localhost:5106 for the Web project, and localhost:5200 for the Public API project once these commands complete. If you have any problems, especially with login, try from a new guest or incognito browser instance.
You can also run the applications by using the instructions located in their `Dockerfile` file in the root of each project. Again, run these commands from the root of the solution (where the .sln file is located).
## Community Extensions
We have some great contributions from the community, and while these aren't maintained by Microsoft we still want to highlight them.
[eShopOnWeb VB.NET](https://github.com/VBAndCs/eShopOnWeb_VB.NET) by Mohammad Hamdy Ghanem
================================================
FILE: azure.yaml
================================================
name: eShopOnWeb
services:
web:
project: ./src/Web
language: csharp
host: appservice
================================================
FILE: docker-compose-webapp.yml
================================================
version: '3.4'
services:
eshopwebmvc:
image: __acr-login-server__/eshopwebmvc
build:
context: .
dockerfile: src/Web/Dockerfile
depends_on:
- "sqlserver"
eshoppublicapi:
image: __acr-login-server__/eshoppublicapi
build:
context: .
dockerfile: src/PublicApi/Dockerfile
depends_on:
- "sqlserver"
sqlserver:
image: mcr.microsoft.com/azure-sql-edge
ports:
- "1433:1433"
environment:
- SA_PASSWORD=@someThingComplicated1234
- ACCEPT_EULA=Y
================================================
FILE: docker-compose.dcproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk" DefaultTargets="Build">
<PropertyGroup Label="Globals">
<ProjectVersion>2.1</ProjectVersion>
<DockerTargetOS>Linux</DockerTargetOS>
<ProjectGuid>{1FCBE191-34FE-4B2E-8915-CA81553958AD}</ProjectGuid>
<DockerLaunchBrowser>True</DockerLaunchBrowser>
<DockerServiceUrl>{Scheme}://localhost:{ServicePort}</DockerServiceUrl>
<DockerServiceName>eshopwebmvc</DockerServiceName>
</PropertyGroup>
<ItemGroup>
<None Include="docker-compose.override.yml">
<DependentUpon>docker-compose.yml</DependentUpon>
</None>
<None Include="docker-compose.yml" />
<None Include=".dockerignore" />
</ItemGroup>
</Project>
================================================
FILE: docker-compose.override.yml
================================================
version: '3.4'
services:
eshopwebmvc:
environment:
- ASPNETCORE_ENVIRONMENT=Docker
- ASPNETCORE_URLS=http://+:8080
ports:
- "5106:8080"
volumes:
- ~/.aspnet/https:/root/.aspnet/https:ro
- ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro
eshoppublicapi:
environment:
- ASPNETCORE_ENVIRONMENT=Docker
- ASPNETCORE_URLS=http://+:8080
ports:
- "5200:8080"
volumes:
- ~/.aspnet/https:/root/.aspnet/https:ro
- ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro
================================================
FILE: docker-compose.yml
================================================
services:
eshopwebmvc:
image: ${DOCKER_REGISTRY-}eshopwebmvc
build:
context: .
dockerfile: src/Web/Dockerfile
depends_on:
- "sqlserver"
eshoppublicapi:
image: ${DOCKER_REGISTRY-}eshoppublicapi
build:
context: .
dockerfile: src/PublicApi/Dockerfile
depends_on:
- "sqlserver"
sqlserver:
image: mcr.microsoft.com/azure-sql-edge
ports:
- "1433:1433"
environment:
- SA_PASSWORD=@someThingComplicated1234
- ACCEPT_EULA=Y
================================================
FILE: eShopOnWeb.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{419A6ACE-0419-4315-A6FB-B0E63D39432E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{227CF035-29B0-448D-97E4-944F9EA850E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{7C461394-ABDC-43CD-A798-71249C58BA67}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationCore", "src\ApplicationCore\ApplicationCore.csproj", "{7FED7440-2311-4D1E-958B-3E887C585CD2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{15EA4737-125B-4E6E-A806-E13B7EBCDCCF}"
ProjectSection(SolutionItems) = preProject
CodeCoverage.runsettings = CodeCoverage.runsettings
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "tests\UnitTests\UnitTests.csproj", "{EF6877E6-59CB-43A7-8C2C-E70DD70CC5B6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{0F576306-7E2D-49B7-87B1-EB5D94CFD5FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "tests\FunctionalTests\FunctionalTests.csproj", "{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BD72BEA-EF42-4B72-8B69-12A39EC76FBA}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Packages.props = Directory.Packages.props
docker-compose.override.yml = docker-compose.override.yml
docker-compose.yml = docker-compose.yml
.github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml
README.md = README.md
EndProjectSection
EndProject
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{1FCBE191-34FE-4B2E-8915-CA81553958AD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PublicApi", "src\PublicApi\PublicApi.csproj", "{B5E4F33C-4667-4A55-AF6A-740F84C4CF3A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorAdmin", "src\BlazorAdmin\BlazorAdmin.csproj", "{71368733-80A4-4869-B215-3A7001878577}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorShared", "src\BlazorShared\BlazorShared.csproj", "{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PublicApiIntegrationTests", "tests\PublicApiIntegrationTests\PublicApiIntegrationTests.csproj", "{D53EF010-8F8C-4337-A059-456E19D8AE63}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{227CF035-29B0-448D-97E4-944F9EA850E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{227CF035-29B0-448D-97E4-944F9EA850E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{227CF035-29B0-448D-97E4-944F9EA850E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{227CF035-29B0-448D-97E4-944F9EA850E5}.Release|Any CPU.Build.0 = Release|Any CPU
{7C461394-ABDC-43CD-A798-71249C58BA67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C461394-ABDC-43CD-A798-71249C58BA67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C461394-ABDC-43CD-A798-71249C58BA67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C461394-ABDC-43CD-A798-71249C58BA67}.Release|Any CPU.Build.0 = Release|Any CPU
{7FED7440-2311-4D1E-958B-3E887C585CD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FED7440-2311-4D1E-958B-3E887C585CD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FED7440-2311-4D1E-958B-3E887C585CD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FED7440-2311-4D1E-958B-3E887C585CD2}.Release|Any CPU.Build.0 = Release|Any CPU
{EF6877E6-59CB-43A7-8C2C-E70DD70CC5B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF6877E6-59CB-43A7-8C2C-E70DD70CC5B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF6877E6-59CB-43A7-8C2C-E70DD70CC5B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF6877E6-59CB-43A7-8C2C-E70DD70CC5B6}.Release|Any CPU.Build.0 = Release|Any CPU
{0F576306-7E2D-49B7-87B1-EB5D94CFD5FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F576306-7E2D-49B7-87B1-EB5D94CFD5FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F576306-7E2D-49B7-87B1-EB5D94CFD5FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F576306-7E2D-49B7-87B1-EB5D94CFD5FC}.Release|Any CPU.Build.0 = Release|Any CPU
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}.Release|Any CPU.Build.0 = Release|Any CPU
{1FCBE191-34FE-4B2E-8915-CA81553958AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FCBE191-34FE-4B2E-8915-CA81553958AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FCBE191-34FE-4B2E-8915-CA81553958AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FCBE191-34FE-4B2E-8915-CA81553958AD}.Release|Any CPU.Build.0 = Release|Any CPU
{B5E4F33C-4667-4A55-AF6A-740F84C4CF3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5E4F33C-4667-4A55-AF6A-740F84C4CF3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5E4F33C-4667-4A55-AF6A-740F84C4CF3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5E4F33C-4667-4A55-AF6A-740F84C4CF3A}.Release|Any CPU.Build.0 = Release|Any CPU
{71368733-80A4-4869-B215-3A7001878577}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71368733-80A4-4869-B215-3A7001878577}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71368733-80A4-4869-B215-3A7001878577}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71368733-80A4-4869-B215-3A7001878577}.Release|Any CPU.Build.0 = Release|Any CPU
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9}.Release|Any CPU.Build.0 = Release|Any CPU
{D53EF010-8F8C-4337-A059-456E19D8AE63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D53EF010-8F8C-4337-A059-456E19D8AE63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D53EF010-8F8C-4337-A059-456E19D8AE63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D53EF010-8F8C-4337-A059-456E19D8AE63}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{227CF035-29B0-448D-97E4-944F9EA850E5} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{7C461394-ABDC-43CD-A798-71249C58BA67} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{7FED7440-2311-4D1E-958B-3E887C585CD2} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{EF6877E6-59CB-43A7-8C2C-E70DD70CC5B6} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
{0F576306-7E2D-49B7-87B1-EB5D94CFD5FC} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
{B5E4F33C-4667-4A55-AF6A-740F84C4CF3A} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{71368733-80A4-4869-B215-3A7001878577} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{715CF7AF-A1EE-40A6-94A0-8DA3F3B2CAE9} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{D53EF010-8F8C-4337-A059-456E19D8AE63} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {49813262-5DA3-4D61-ABD3-493C74CE8C2B}
EndGlobalSection
EndGlobal
================================================
FILE: global.json
================================================
{
"sdk": {
"version": "8.0.x",
"rollForward": "latestFeature"
}
}
================================================
FILE: infra/abbreviations.json
================================================
{
"analysisServicesServers": "as",
"apiManagementService": "apim-",
"appConfigurationConfigurationStores": "appcs-",
"appManagedEnvironments": "cae-",
"appContainerApps": "ca-",
"authorizationPolicyDefinitions": "policy-",
"automationAutomationAccounts": "aa-",
"blueprintBlueprints": "bp-",
"blueprintBlueprintsArtifacts": "bpa-",
"cacheRedis": "redis-",
"cdnProfiles": "cdnp-",
"cdnProfilesEndpoints": "cdne-",
"cognitiveServicesAccounts": "cog-",
"cognitiveServicesFormRecognizer": "cog-fr-",
"cognitiveServicesTextAnalytics": "cog-ta-",
"computeAvailabilitySets": "avail-",
"computeCloudServices": "cld-",
"computeDiskEncryptionSets": "des",
"computeDisks": "disk",
"computeDisksOs": "osdisk",
"computeGalleries": "gal",
"computeSnapshots": "snap-",
"computeVirtualMachines": "vm",
"computeVirtualMachineScaleSets": "vmss-",
"containerInstanceContainerGroups": "ci",
"containerRegistryRegistries": "cr",
"containerServiceManagedClusters": "aks-",
"databricksWorkspaces": "dbw-",
"dataFactoryFactories": "adf-",
"dataLakeAnalyticsAccounts": "dla",
"dataLakeStoreAccounts": "dls",
"dataMigrationServices": "dms-",
"dBforMySQLServers": "mysql-",
"dBforPostgreSQLServers": "psql-",
"devicesIotHubs": "iot-",
"devicesProvisioningServices": "provs-",
"devicesProvisioningServicesCertificates": "pcert-",
"documentDBDatabaseAccounts": "cosmos-",
"eventGridDomains": "evgd-",
"eventGridDomainsTopics": "evgt-",
"eventGridEventSubscriptions": "evgs-",
"eventHubNamespaces": "evhns-",
"eventHubNamespacesEventHubs": "evh-",
"hdInsightClustersHadoop": "hadoop-",
"hdInsightClustersHbase": "hbase-",
"hdInsightClustersKafka": "kafka-",
"hdInsightClustersMl": "mls-",
"hdInsightClustersSpark": "spark-",
"hdInsightClustersStorm": "storm-",
"hybridComputeMachines": "arcs-",
"insightsActionGroups": "ag-",
"insightsComponents": "appi-",
"keyVaultVaults": "kv-",
"kubernetesConnectedClusters": "arck",
"kustoClusters": "dec",
"kustoClustersDatabases": "dedb",
"logicIntegrationAccounts": "ia-",
"logicWorkflows": "logic-",
"machineLearningServicesWorkspaces": "mlw-",
"managedIdentityUserAssignedIdentities": "id-",
"managementManagementGroups": "mg-",
"migrateAssessmentProjects": "migr-",
"networkApplicationGateways": "agw-",
"networkApplicationSecurityGroups": "asg-",
"networkAzureFirewalls": "afw-",
"networkBastionHosts": "bas-",
"networkConnections": "con-",
"networkDnsZones": "dnsz-",
"networkExpressRouteCircuits": "erc-",
"networkFirewallPolicies": "afwp-",
"networkFirewallPoliciesWebApplication": "waf",
"networkFirewallPoliciesRuleGroups": "wafrg",
"networkFrontDoors": "fd-",
"networkFrontdoorWebApplicationFirewallPolicies": "fdfp-",
"networkLoadBalancersExternal": "lbe-",
"networkLoadBalancersInternal": "lbi-",
"networkLoadBalancersInboundNatRules": "rule-",
"networkLocalNetworkGateways": "lgw-",
"networkNatGateways": "ng-",
"networkNetworkInterfaces": "nic-",
"networkNetworkSecurityGroups": "nsg-",
"networkNetworkSecurityGroupsSecurityRules": "nsgsr-",
"networkNetworkWatchers": "nw-",
"networkPrivateDnsZones": "pdnsz-",
"networkPrivateLinkServices": "pl-",
"networkPublicIPAddresses": "pip-",
"networkPublicIPPrefixes": "ippre-",
"networkRouteFilters": "rf-",
"networkRouteTables": "rt-",
"networkRouteTablesRoutes": "udr-",
"networkTrafficManagerProfiles": "traf-",
"networkVirtualNetworkGateways": "vgw-",
"networkVirtualNetworks": "vnet-",
"networkVirtualNetworksSubnets": "snet-",
"networkVirtualNetworksVirtualNetworkPeerings": "peer-",
"networkVirtualWans": "vwan-",
"networkVpnGateways": "vpng-",
"networkVpnGatewaysVpnConnections": "vcn-",
"networkVpnGatewaysVpnSites": "vst-",
"notificationHubsNamespaces": "ntfns-",
"notificationHubsNamespacesNotificationHubs": "ntf-",
"operationalInsightsWorkspaces": "log-",
"portalDashboards": "dash-",
"powerBIDedicatedCapacities": "pbi-",
"purviewAccounts": "pview-",
"recoveryServicesVaults": "rsv-",
"resourcesResourceGroups": "rg-",
"searchSearchServices": "srch-",
"serviceBusNamespaces": "sb-",
"serviceBusNamespacesQueues": "sbq-",
"serviceBusNamespacesTopics": "sbt-",
"serviceEndPointPolicies": "se-",
"serviceFabricClusters": "sf-",
"signalRServiceSignalR": "sigr",
"sqlManagedInstances": "sqlmi-",
"sqlServers": "sql-",
"sqlServersDataWarehouse": "sqldw-",
"sqlServersDatabases": "sqldb-",
"sqlServersDatabasesStretch": "sqlstrdb-",
"storageStorageAccounts": "st",
"storageStorageAccountsVm": "stvm",
"storSimpleManagers": "ssimp",
"streamAnalyticsCluster": "asa-",
"synapseWorkspaces": "syn",
"synapseWorkspacesAnalyticsWorkspaces": "synw",
"synapseWorkspacesSqlPoolsDedicated": "syndp",
"synapseWorkspacesSqlPoolsSpark": "synsp",
"timeSeriesInsightsEnvironments": "tsi-",
"webServerFarms": "plan-",
"webSitesAppService": "app-",
"webSitesAppServiceEnvironment": "ase-",
"webSitesFunctions": "func-",
"webStaticSites": "stapp-"
}
================================================
FILE: infra/aci.bicep
================================================
@description('Name for the container group')
param name string = 'eshopcontainer'
@description('Location for all resources.')
param location string = resourceGroup().location
@description('Container image to deploy. Should be of the form repoName/imagename:tag for images stored in public Docker Hub, or a fully qualified URI for other registries. Images from private registries require additional registry credentials.')
param image string = 'mcr.microsoft.com/azuredocs/aci-helloworld'
@description('Port to open on the container and the public IP address.')
param port int = 5106
@description('The number of CPU cores to allocate to the container.')
param cpuCores int = 1
@description('The amount of memory to allocate to the container in gigabytes.')
param memoryInGb int = 2
@description('The behavior of Azure runtime if container has stopped.')
@allowed([
'Always'
'Never'
'OnFailure'
])
param restartPolicy string = 'Always'
@secure()
param password string
param username string
param server string
resource containerGroup 'Microsoft.ContainerInstance/containerGroups@2021-09-01' = {
name: name
location: location
properties: {
containers: [
{
name: name
properties: {
image: image
ports: [
{
port: port
protocol: 'TCP'
}
{
port: 80
protocol: 'TCP'
}
]
resources: {
requests: {
cpu: cpuCores
memoryInGB: memoryInGb
}
}
environmentVariables: [
{
name: 'ASPNETCORE_ENVIRONMENT'
value: 'Docker'
}
{
name: 'UseOnlyInMemoryDatabase'
value: 'true'
}
{
name: 'ASPNETCORE_HTTP_PORTS'
value: '80'
}
]
}
}
]
osType: 'Linux'
restartPolicy: restartPolicy
ipAddress: {
type: 'Public'
ports: [
{
port: port
protocol: 'TCP'
}
{
port: 80
protocol: 'TCP'
}
]
}
imageRegistryCredentials: [
{
password: password
server: server
username: username
}
]
}
}
output containerIPv4Address string = containerGroup.properties.ipAddress.ip
================================================
FILE: infra/acr.bicep
================================================
@description('Generate a Suffix based on the Resource Group ID')
param suffix string = uniqueString(resourceGroup().id)
@description('Use the Resource Group Location')
param location string = resourceGroup().location
resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
name: 'cr${suffix}'
location: location
sku: {
name: 'Basic'
}
properties: {
adminUserEnabled: false
}
}
@description('Output the login server property for later use')
output acrLoginServer string = acr.properties.loginServer
================================================
FILE: infra/core/database/sqlserver/sqlserver.bicep
================================================
param name string
param location string = resourceGroup().location
param tags object = {}
param appUser string = 'appUser'
param databaseName string
param keyVaultName string
param sqlAdmin string = 'sqlAdmin'
param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING'
@secure()
param sqlAdminPassword string
@secure()
param appUserPassword string
resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
name: name
location: location
tags: tags
properties: {
version: '12.0'
minimalTlsVersion: '1.2'
publicNetworkAccess: 'Enabled'
administratorLogin: sqlAdmin
administratorLoginPassword: sqlAdminPassword
}
resource database 'databases' = {
name: databaseName
location: location
}
resource firewall 'firewallRules' = {
name: 'Azure Services'
properties: {
// Allow all clients
// Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only".
// This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes.
startIpAddress: '0.0.0.1'
endIpAddress: '255.255.255.254'
}
}
}
resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: '${name}-deployment-script'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.37.0'
retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running
timeout: 'PT5M' // Five minutes
cleanupPreference: 'OnSuccess'
environmentVariables: [
{
name: 'APPUSERNAME'
value: appUser
}
{
name: 'APPUSERPASSWORD'
secureValue: appUserPassword
}
{
name: 'DBNAME'
value: databaseName
}
{
name: 'DBSERVER'
value: sqlServer.properties.fullyQualifiedDomainName
}
{
name: 'SQLCMDPASSWORD'
secureValue: sqlAdminPassword
}
{
name: 'SQLADMIN'
value: sqlAdmin
}
]
scriptContent: '''
wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2
tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C .
cat <<SCRIPT_END > ./initDb.sql
drop user ${APPUSERNAME}
go
create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}'
go
alter role db_owner add member ${APPUSERNAME}
go
SCRIPT_END
./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql
'''
}
}
resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
parent: keyVault
name: 'sqlAdminPassword'
properties: {
value: sqlAdminPassword
}
}
resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
parent: keyVault
name: 'appUserPassword'
properties: {
value: appUserPassword
}
}
resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
parent: keyVault
name: connectionStringKey
properties: {
value: '${connectionString}; Password=${appUserPassword}'
}
}
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
name: keyVaultName
}
var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}'
output connectionStringKey string = connectionStringKey
output databaseName string = sqlServer::database.name
================================================
FILE: infra/core/host/appservice.bicep
================================================
param name string
param location string = resourceGroup().location
param tags object = {}
// Reference Properties
param applicationInsightsName string = ''
param appServicePlanId string
param keyVaultName string = ''
param managedIdentity bool = !empty(keyVaultName)
// Runtime Properties
@allowed([
'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom'
])
param runtimeName string
param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}'
param runtimeVersion string
// Microsoft.Web/sites Properties
param kind string = 'app,linux'
// Microsoft.Web/sites/config
param allowedOrigins array = []
param alwaysOn bool = true
param appCommandLine string = ''
param appSettings object = {}
param clientAffinityEnabled bool = false
param enableOryxBuild bool = contains(kind, 'linux')
param functionAppScaleLimit int = -1
param linuxFxVersion string = runtimeNameAndVersion
param minimumElasticInstanceCount int = -1
param numberOfWorkers int = -1
param scmDoBuildDuringDeployment bool = false
param use32BitWorkerProcess bool = false
param ftpsState string = 'FtpsOnly'
param healthCheckPath string = ''
resource appService 'Microsoft.Web/sites@2022-03-01' = {
name: name
location: location
tags: tags
kind: kind
properties: {
serverFarmId: appServicePlanId
siteConfig: {
linuxFxVersion: linuxFxVersion
alwaysOn: alwaysOn
ftpsState: ftpsState
minTlsVersion: '1.2'
appCommandLine: appCommandLine
numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null
minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null
use32BitWorkerProcess: use32BitWorkerProcess
functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null
healthCheckPath: healthCheckPath
cors: {
allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins)
}
}
clientAffinityEnabled: clientAffinityEnabled
httpsOnly: true
}
identity: { type: managedIdentity ? 'SystemAssigned' : 'None' }
resource configAppSettings 'config' = {
name: 'appsettings'
properties: union(appSettings,
{
SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment)
ENABLE_ORYX_BUILD: string(enableOryxBuild)
},
!empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {},
!empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {})
}
resource configLogs 'config' = {
name: 'logs'
properties: {
applicationLogs: { fileSystem: { level: 'Verbose' } }
detailedErrorMessages: { enabled: true }
failedRequestsTracing: { enabled: true }
httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } }
}
dependsOn: [
configAppSettings
]
}
}
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) {
name: keyVaultName
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) {
name: applicationInsightsName
}
output identityPrincipalId string = managedIdentity ? appService.identity.principalId : ''
output name string = appService.name
output uri string = 'https://${appService.properties.defaultHostName}'
================================================
FILE: infra/core/host/appserviceplan.bicep
================================================
param name string
param location string = resourceGroup().location
param tags object = {}
param kind string = ''
param reserved bool = true
param sku object
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: name
location: location
tags: tags
sku: sku
kind: kind
properties: {
reserved: reserved
}
}
output id string = appServicePlan.id
================================================
FILE: infra/core/security/keyvault-access.bicep
================================================
param name string = 'add'
param keyVaultName string
param permissions object = { secrets: [ 'get', 'list' ] }
param principalId string
resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = {
parent: keyVault
name: name
properties: {
accessPolicies: [ {
objectId: principalId
tenantId: subscription().tenantId
permissions: permissions
} ]
}
}
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
name: keyVaultName
}
================================================
FILE: infra/core/security/keyvault.bicep
================================================
param name string
param location string = resourceGroup().location
param tags object = {}
param principalId string = ''
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: name
location: location
tags: tags
properties: {
tenantId: subscription().tenantId
sku: { family: 'A', name: 'standard' }
accessPolicies: !empty(principalId) ? [
{
objectId: principalId
permissions: { secrets: [ 'get', 'list' ] }
tenantId: subscription().tenantId
}
] : []
}
}
output endpoint string = keyVault.properties.vaultUri
output name string = keyVault.name
================================================
FILE: infra/main.bicep
================================================
targetScope = 'subscription'
@minLength(1)
@maxLength(64)
@description('Name of the the environment which is used to generate a short unique hash used in all resources.')
param environmentName string
@minLength(1)
@description('Primary location for all resources')
param location string
// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,:
// "resourceGroupName": {
// "value": "myGroupName"
// }
param resourceGroupName string = ''
param webServiceName string = ''
param catalogDatabaseName string = 'catalogDatabase'
param catalogDatabaseServerName string = ''
param identityDatabaseName string = 'identityDatabase'
param identityDatabaseServerName string = ''
param appServicePlanName string = ''
param keyVaultName string = ''
@description('Id of the user or app to assign application roles')
param principalId string = ''
@secure()
@description('SQL Server administrator password')
param sqlAdminPassword string
@secure()
@description('Application user password')
param appUserPassword string
var abbrs = loadJsonContent('./abbreviations.json')
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
var tags = { 'azd-env-name': environmentName }
// Organize resources in a resource group
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}'
location: location
tags: tags
}
// The application frontend
module web './core/host/appservice.bicep' = {
name: 'web'
scope: rg
params: {
name: !empty(webServiceName) ? webServiceName : '${abbrs.webSitesAppService}web-${resourceToken}'
location: location
appServicePlanId: appServicePlan.outputs.id
keyVaultName: keyVault.outputs.name
runtimeName: 'dotnetcore'
runtimeVersion: '8.0'
tags: union(tags, { 'azd-service-name': 'web' })
appSettings: {
AZURE_SQL_CATALOG_CONNECTION_STRING_KEY: 'AZURE-SQL-CATALOG-CONNECTION-STRING'
AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY: 'AZURE-SQL-IDENTITY-CONNECTION-STRING'
AZURE_KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint
}
}
}
module apiKeyVaultAccess './core/security/keyvault-access.bicep' = {
name: 'api-keyvault-access'
scope: rg
params: {
keyVaultName: keyVault.outputs.name
principalId: web.outputs.identityPrincipalId
}
}
// The application database: Catalog
module catalogDb './core/database/sqlserver/sqlserver.bicep' = {
name: 'sql-catalog'
scope: rg
params: {
name: !empty(catalogDatabaseServerName) ? catalogDatabaseServerName : '${abbrs.sqlServers}catalog-${resourceToken}'
databaseName: catalogDatabaseName
location: location
tags: tags
sqlAdminPassword: sqlAdminPassword
appUserPassword: appUserPassword
keyVaultName: keyVault.outputs.name
connectionStringKey: 'AZURE-SQL-CATALOG-CONNECTION-STRING'
}
}
// The application database: Identity
module identityDb './core/database/sqlserver/sqlserver.bicep' = {
name: 'sql-identity'
scope: rg
params: {
name: !empty(identityDatabaseServerName) ? identityDatabaseServerName : '${abbrs.sqlServers}identity-${resourceToken}'
databaseName: identityDatabaseName
location: location
tags: tags
sqlAdminPassword: sqlAdminPassword
appUserPassword: appUserPassword
keyVaultName: keyVault.outputs.name
connectionStringKey: 'AZURE-SQL-IDENTITY-CONNECTION-STRING'
}
}
// Store secrets in a keyvault
module keyVault './core/security/keyvault.bicep' = {
name: 'keyvault'
scope: rg
params: {
name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}'
location: location
tags: tags
principalId: principalId
}
}
// Create an App Service Plan to group applications under the same payment plan and SKU
module appServicePlan './core/host/appserviceplan.bicep' = {
name: 'appserviceplan'
scope: rg
params: {
name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}'
location: location
tags: tags
sku: {
name: 'B1'
}
}
}
// Data outputs
output AZURE_SQL_CATALOG_CONNECTION_STRING_KEY string = catalogDb.outputs.connectionStringKey
output AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY string = identityDb.outputs.connectionStringKey
output AZURE_SQL_CATALOG_DATABASE_NAME string = catalogDb.outputs.databaseName
output AZURE_SQL_IDENTITY_DATABASE_NAME string = identityDb.outputs.databaseName
// App outputs
output AZURE_LOCATION string = location
output AZURE_TENANT_ID string = tenant().tenantId
output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint
output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name
================================================
FILE: infra/main.parameters.json
================================================
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"value": "${AZURE_ENV_NAME}"
},
"location": {
"value": "${AZURE_LOCATION}"
},
"principalId": {
"value": "${AZURE_PRINCIPAL_ID}"
},
"sqlAdminPassword": {
"value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} sqlAdminPassword)"
},
"appUserPassword": {
"value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} appUserPassword)"
}
}
}
================================================
FILE: infra/simple-windows-vm.bicep
================================================
@description('Username for the Virtual Machine.')
param adminUsername string = 'Student'
@description('Password for the Virtual Machine.')
@minLength(12)
@secure()
param adminPassword string = 'P@s${uniqueString(newGuid())}!'
@description('Unique DNS Name for the Public IP used to access the Virtual Machine.')
param dnsLabelPrefix string = toLower('${vmName}-${uniqueString(resourceGroup().id, vmName)}')
@description('Name for the Public IP used to access the Virtual Machine.')
param publicIpName string = 'myPublicIP'
@description('Allocation method for the Public IP used to access the Virtual Machine.')
@allowed([
'Dynamic'
'Static'
])
param publicIPAllocationMethod string = 'Dynamic'
@description('SKU for the Public IP used to access the Virtual Machine.')
@allowed([
'Basic'
'Standard'
])
param publicIpSku string = 'Basic'
@description('The Windows version for the VM. This will pick a fully patched image of this given Windows version.')
@allowed([
'2016-datacenter-gensecond'
'2016-datacenter-server-core-g2'
'2016-datacenter-server-core-smalldisk-g2'
'2016-datacenter-smalldisk-g2'
'2016-datacenter-with-containers-g2'
'2016-datacenter-zhcn-g2'
'2019-datacenter-core-g2'
'2019-datacenter-core-smalldisk-g2'
'2019-datacenter-core-with-containers-g2'
'2019-datacenter-core-with-containers-smalldisk-g2'
'2019-datacenter-gensecond'
'2019-datacenter-smalldisk-g2'
'2019-datacenter-with-containers-g2'
'2019-datacenter-with-containers-smalldisk-g2'
'2019-datacenter-zhcn-g2'
'2022-datacenter-azure-edition'
'2022-datacenter-azure-edition-core'
'2022-datacenter-azure-edition-core-smalldisk'
'2022-datacenter-azure-edition-smalldisk'
'2022-datacenter-core-g2'
'2022-datacenter-core-smalldisk-g2'
'2022-datacenter-g2'
'2022-datacenter-smalldisk-g2'
])
param OSVersion string = '2022-datacenter-azure-edition'
@description('Size of the virtual machine.')
param vmSize string = 'Standard_B1ms'
@description('Location for all resources.')
param location string = resourceGroup().location
@description('Name of the virtual machine.')
param vmName string = 'simple-vm'
@description('Security Type of the Virtual Machine.')
@allowed([
'Standard'
'TrustedLaunch'
])
param securityType string = 'TrustedLaunch'
var storageAccountName = 'bootdiags${uniqueString(resourceGroup().id)}'
var nicName = 'myVMNic'
var addressPrefix = '10.0.0.0/16'
var subnetName = 'Subnet'
var subnetPrefix = '10.0.0.0/24'
var virtualNetworkName = 'MyVNET'
var networkSecurityGroupName = 'default-NSG'
var securityProfileJson = {
uefiSettings: {
secureBootEnabled: true
vTpmEnabled: true
}
securityType: securityType
}
var extensionName = 'GuestAttestation'
var extensionPublisher = 'Microsoft.Azure.Security.WindowsAttestation'
var extensionVersion = '1.0'
var maaTenantName = 'GuestAttestation'
var maaEndpoint = substring('emptyString', 0, 0)
resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'Storage'
}
resource publicIp 'Microsoft.Network/publicIPAddresses@2022-05-01' = {
name: publicIpName
location: location
sku: {
name: publicIpSku
}
properties: {
publicIPAllocationMethod: publicIPAllocationMethod
dnsSettings: {
domainNameLabel: dnsLabelPrefix
}
}
}
resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-05-01' = {
name: networkSecurityGroupName
location: location
properties: {
securityRules: [
{
name: 'default-allow-3389'
properties: {
priority: 1000
access: 'Allow'
direction: 'Inbound'
destinationPortRange: '3389'
protocol: 'Tcp'
sourcePortRange: '*'
sourceAddressPrefix: '*'
destinationAddressPrefix: '*'
}
}
]
}
}
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-05-01' = {
name: virtualNetworkName
location: location
properties: {
addressSpace: {
addressPrefixes: [
addressPrefix
]
}
subnets: [
{
name: subnetName
properties: {
addressPrefix: subnetPrefix
networkSecurityGroup: {
id: networkSecurityGroup.id
}
}
}
]
}
}
resource nic 'Microsoft.Network/networkInterfaces@2022-05-01' = {
name: nicName
location: location
properties: {
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
privateIPAllocationMethod: 'Dynamic'
publicIPAddress: {
id: publicIp.id
}
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetworkName, subnetName)
}
}
}
]
}
dependsOn: [
virtualNetwork
]
}
resource vm 'Microsoft.Compute/virtualMachines@2022-03-01' = {
name: vmName
location: location
properties: {
hardwareProfile: {
vmSize: vmSize
}
osProfile: {
computerName: vmName
adminUsername: adminUsername
adminPassword: adminPassword
}
storageProfile: {
imageReference: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: OSVersion
version: 'latest'
}
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: 'StandardSSD_LRS'
}
}
dataDisks: [
{
diskSizeGB: 1023
lun: 0
createOption: 'Empty'
}
]
}
networkProfile: {
networkInterfaces: [
{
id: nic.id
}
]
}
diagnosticsProfile: {
bootDiagnostics: {
enabled: true
storageUri: storageAccount.properties.primaryEndpoints.blob
}
}
securityProfile: ((securityType == 'TrustedLaunch') ? securityProfileJson : null)
}
}
resource vmExtension 'Microsoft.Compute/virtualMachines/extensions@2022-03-01' = if ((securityType == 'TrustedLaunch') && ((securityProfileJson.uefiSettings.secureBootEnabled == true) && (securityProfileJson.uefiSettings.vTpmEnabled == true))) {
parent: vm
name: extensionName
location: location
properties: {
publisher: extensionPublisher
type: extensionName
typeHandlerVersion: extensionVersion
autoUpgradeMinorVersion: true
enableAutomaticUpgrade: true
settings: {
AttestationConfig: {
MaaSettings: {
maaEndpoint: maaEndpoint
maaTenantName: maaTenantName
}
}
}
}
}
output hostname string = publicIp.properties.dnsSettings.fqdn
================================================
FILE: infra/webapp-docker.bicep
================================================
@description('Generate a Suffix based on the Resource Group ID')
param suffix string = uniqueString(resourceGroup().id)
@description('Use the Resource Group Location')
param location string = resourceGroup().location
resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' existing = {
name: 'cr${suffix}'
}
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: 'asp-${suffix}'
location: location
kind: 'linux'
properties: {
reserved: true
}
sku: {
name: 'B1'
}
}
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: 'app-${suffix}'
location: location
tags: {}
properties: {
siteConfig: {
acrUseManagedIdentityCreds: true
appSettings: [
{
name: 'UseOnlyInMemoryDatabase'
value: 'true'
}
{
name: 'ASPNETCORE_ENVIRONMENT'
value: 'Docker'
}
{
name: 'ASPNETCORE_HTTP_PORTS'
value: '80'
}
]
linuxFxVersion: 'DOCKER|${acr.properties.loginServer}/eshoponweb/web:latest'
}
serverFarmId: appServicePlan.id
}
identity: {
type: 'SystemAssigned'
}
}
================================================
FILE: infra/webapp-to-acr-roleassignment.bicep
================================================
@description('Generate a Suffix based on the Resource Group ID')
param suffix string = uniqueString(resourceGroup().id)
@description('Set the ACR Pull Role Definition ID')
param acrPullRoleDefinitionID string = '7f951dda-4ed3-4680-a7ca-43fe172d538d'
@description('Generate a unique GUID to use as name for the role assignment')
var webAppToAcrRoleAssignmentName = guid(webApp.id, acrPullRoleDefinitionID, acr.id)
resource acr 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = {
name: 'cr${suffix}'
}
resource webApp 'Microsoft.Web/sites@2022-03-01' existing = {
name: 'app-${suffix}'
}
resource webAppToAcrRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: acr
name: webAppToAcrRoleAssignmentName
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', acrPullRoleDefinitionID)
principalId: webApp.identity.principalId
}
}
================================================
FILE: infra/webapp.bicep
================================================
param webAppName string // = uniqueString(resourceGroup().id) // unique String gets created from az cli instructions
param sku string = 'S1' // The SKU of App Service Plan
param location string = resourceGroup().location
var appServicePlanName = toLower('AppServicePlan-${webAppName}')
resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
name: appServicePlanName
location: location
properties: {
reserved: true
}
sku: {
name: sku
}
}
resource appService 'Microsoft.Web/sites@2022-09-01' = {
name: webAppName
kind: 'app'
location: location
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'DOTNETCORE|8.0'
appSettings: [
{
name: 'ASPNETCORE_ENVIRONMENT'
value: 'Development'
}
{
name: 'UseOnlyInMemoryDatabase'
value: 'true'
}
]
}
}
}
================================================
FILE: src/ApplicationCore/ApplicationCore.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" />
<PackageReference Include="Ardalis.Result" />
<PackageReference Include="Ardalis.Specification" />
<PackageReference Include="System.Security.Claims" />
<PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BlazorShared\BlazorShared.csproj" />
</ItemGroup>
</Project>
================================================
FILE: src/ApplicationCore/CatalogSettings.cs
================================================
namespace Microsoft.eShopWeb;
public class CatalogSettings
{
public string? CatalogBaseUrl { get; set; }
}
================================================
FILE: src/ApplicationCore/Constants/AuthorizationConstants.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Constants;
public class AuthorizationConstants
{
public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes";
// 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";
}
================================================
FILE: src/ApplicationCore/Entities/BaseEntity.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
// This can easily be modified to be BaseEntity<T> 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; }
}
================================================
FILE: src/ApplicationCore/Entities/BasketAggregate/Basket.cs
================================================
using System.Collections.Generic;
using System.Linq;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
public class Basket : BaseEntity, IAggregateRoot
{
public string BuyerId { get; private set; }
private readonly List<BasketItem> _items = new List<BasketItem>();
public IReadOnlyCollection<BasketItem> Items => _items.AsReadOnly();
public int TotalItems => _items.Sum(i => i.Quantity);
public Basket(string buyerId)
{
BuyerId = buyerId;
}
public void AddItem(int catalogItemId, decimal unitPrice, int quantity = 1)
{
if (!Items.Any(i => i.CatalogItemId == catalogItemId))
{
_items.Add(new BasketItem(catalogItemId, quantity, unitPrice));
return;
}
var existingItem = Items.First(i => i.CatalogItemId == catalogItemId);
existingItem.AddQuantity(quantity);
}
public void RemoveEmptyItems()
{
_items.RemoveAll(i => i.Quantity == 0);
}
public void SetNewBuyerId(string buyerId)
{
BuyerId = buyerId;
}
}
================================================
FILE: src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs
================================================
using Ardalis.GuardClauses;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
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 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);
Quantity += quantity;
}
public void SetQuantity(int quantity)
{
Guard.Against.OutOfRange(quantity, nameof(quantity), 0, int.MaxValue);
Quantity = quantity;
}
}
================================================
FILE: src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs
================================================
using System.Collections.Generic;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate;
public class Buyer : BaseEntity, IAggregateRoot
{
public string IdentityGuid { get; private set; }
private List<PaymentMethod> _paymentMethods = new List<PaymentMethod>();
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
#pragma warning disable CS8618 // Required by Entity Framework
private Buyer() { }
public Buyer(string identity) : this()
{
Guard.Against.NullOrEmpty(identity, nameof(identity));
IdentityGuid = identity;
}
}
================================================
FILE: src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate;
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; }
}
================================================
FILE: src/ApplicationCore/Entities/CatalogBrand.cs
================================================
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
public class CatalogBrand : BaseEntity, IAggregateRoot
{
public string Brand { get; private set; }
public CatalogBrand(string brand)
{
Brand = brand;
}
}
================================================
FILE: src/ApplicationCore/Entities/CatalogItem.cs
================================================
using System;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
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 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(CatalogItemDetails details)
{
Guard.Against.NullOrEmpty(details.Name, nameof(details.Name));
Guard.Against.NullOrEmpty(details.Description, nameof(details.Description));
Guard.Against.NegativeOrZero(details.Price, nameof(details.Price));
Name = details.Name;
Description = details.Description;
Price = details.Price;
}
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 UpdatePictureUri(string pictureName)
{
if (string.IsNullOrEmpty(pictureName))
{
PictureUri = string.Empty;
return;
}
PictureUri = $"images\\products\\{pictureName}?{new DateTime().Ticks}";
}
public readonly record struct CatalogItemDetails
{
public string? Name { get; }
public string? Description { get; }
public decimal Price { get; }
public CatalogItemDetails(string? name, string? description, decimal price)
{
Name = name;
Description = description;
Price = price;
}
}
}
================================================
FILE: src/ApplicationCore/Entities/CatalogType.cs
================================================
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
namespace Microsoft.eShopWeb.ApplicationCore.Entities;
public class CatalogType : BaseEntity, IAggregateRoot
{
public string Type { get; private set; }
public CatalogType(string type)
{
Type = type;
}
}
================================================
FILE: src/ApplicationCore/Entities/EshopDiagram.cd
================================================
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="Microsoft.eShopWeb.ApplicationCore.Entities.CatalogBrand" BaseTypeListCollapsed="true">
<Position X="3" Y="7.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAA=</HashCode>
<FileName>Entities\CatalogBrand.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.377" Collapsed="true" />
</Class>
<Class Name="Microsoft.eShopWeb.ApplicationCore.Entities.CatalogItem" BaseTypeListCollapsed="true">
<Position X="4.75" Y="3.5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAgAAAAAA4AgAwAAAAAAAAQAAAEAAAAAAAAAAQAACQA=</HashCode>
<FileName>Entities\CatalogItem.cs</FileName>
</TypeIdentifier>
<ShowAsAssociation>
<Property Name="CatalogBrand" />
<Property Name="CatalogType" />
</ShowAsAssociation>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="Microsoft.eShopWeb.ApplicationCore.Entities.CatalogType" BaseTypeListCollapsed="true">
<Position X="6.5" Y="7.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAA=</HashCode>
<FileName>Entities\CatalogType.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>
================================================
FILE: src/ApplicationCore/Entities/OrderAggregate/Address.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
public class Address // ValueObject
{
public string Street { get; private set; }
public string City { get; private set; }
public string State { get; private set; }
public string Country { get; private set; }
public string ZipCode { get; private set; }
#pragma warning disable CS8618 // Required by Entity Framework
private Address() { }
public Address(string street, string city, string state, string country, string zipcode)
{
Street = street;
City = city;
State = state;
Country = country;
ZipCode = zipcode;
}
}
================================================
FILE: src/ApplicationCore/Entities/OrderAggregate/CatalogItemOrdered.cs
================================================
using Ardalis.GuardClauses;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
/// <summary>
/// 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.
/// </summary>
public class CatalogItemOrdered // ValueObject
{
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;
}
#pragma warning disable CS8618 // Required by Entity Framework
private CatalogItemOrdered() {}
public int CatalogItemId { get; private set; }
public string ProductName { get; private set; }
public string PictureUri { get; private set; }
}
================================================
FILE: src/ApplicationCore/Entities/OrderAggregate/Order.cs
================================================
using System;
using System.Collections.Generic;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
public class Order : BaseEntity, IAggregateRoot
{
#pragma warning disable CS8618 // Required by Entity Framework
private Order() {}
public Order(string buyerId, Address shipToAddress, List<OrderItem> items)
{
Guard.Against.NullOrEmpty(buyerId, nameof(buyerId));
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; }
// 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<OrderItem> _orderItems = new List<OrderItem>();
// 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<OrderItem> OrderItems => _orderItems.AsReadOnly();
public decimal Total()
{
var total = 0m;
foreach (var item in _orderItems)
{
total += item.UnitPrice * item.Units;
}
return total;
}
}
================================================
FILE: src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
public class OrderItem : BaseEntity
{
public CatalogItemOrdered ItemOrdered { get; private set; }
public decimal UnitPrice { get; private set; }
public int Units { get; private set; }
#pragma warning disable CS8618 // Required by Entity Framework
private OrderItem() {}
public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units)
{
ItemOrdered = itemOrdered;
UnitPrice = unitPrice;
Units = units;
}
}
================================================
FILE: src/ApplicationCore/Exceptions/BasketNotFoundException.cs
================================================
using System;
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
public class BasketNotFoundException : Exception
{
public BasketNotFoundException(int basketId) : base($"No basket found with id {basketId}")
{
}
}
================================================
FILE: src/ApplicationCore/Exceptions/DuplicateException.cs
================================================
using System;
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
public class DuplicateException : Exception
{
public DuplicateException(string message) : base(message)
{
}
}
================================================
FILE: src/ApplicationCore/Exceptions/EmptyBasketOnCheckoutException.cs
================================================
using System;
namespace Microsoft.eShopWeb.ApplicationCore.Exceptions;
public class EmptyBasketOnCheckoutException : Exception
{
public EmptyBasketOnCheckoutException()
: base($"Basket cannot have 0 items on checkout")
{
}
public EmptyBasketOnCheckoutException(string message) : base(message)
{
}
public EmptyBasketOnCheckoutException(string message, Exception innerException) : base(message, innerException)
{
}
}
================================================
FILE: src/ApplicationCore/Extensions/GuardExtensions.cs
================================================
using System.Collections.Generic;
using System.Linq;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
namespace Ardalis.GuardClauses;
public static class BasketGuards
{
public static void EmptyBasketOnCheckout(this IGuardClause guardClause, IReadOnlyCollection<BasketItem> basketItems)
{
if (!basketItems.Any())
throw new EmptyBasketOnCheckoutException();
}
}
================================================
FILE: src/ApplicationCore/Extensions/JsonExtensions.cs
================================================
using System.Text.Json;
namespace Microsoft.eShopWeb;
public static class JsonExtensions
{
private static readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
public static T? FromJson<T>(this string json) =>
JsonSerializer.Deserialize<T>(json, _jsonOptions);
public static string ToJson<T>(this T obj) =>
JsonSerializer.Serialize<T>(obj, _jsonOptions);
}
================================================
FILE: src/ApplicationCore/Interfaces/IAggregateRoot.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface IAggregateRoot
{ }
================================================
FILE: src/ApplicationCore/Interfaces/IAppLogger.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
/// <summary>
/// This type eliminates the need to depend directly on the ASP.NET Core logging types.
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IAppLogger<T>
{
void LogInformation(string message, params object[] args);
void LogWarning(string message, params object[] args);
}
================================================
FILE: src/ApplicationCore/Interfaces/IBasketQueryService.cs
================================================
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
/// <summary>
/// Specific query used to fetch count without running in memory
/// </summary>
public interface IBasketQueryService
{
Task<int> CountTotalBasketItems(string username);
}
================================================
FILE: src/ApplicationCore/Interfaces/IBasketService.cs
================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Ardalis.Result;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface IBasketService
{
Task TransferBasketAsync(string anonymousId, string userName);
Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1);
Task<Result<Basket>> SetQuantities(int basketId, Dictionary<string, int> quantities);
Task DeleteBasketAsync(int basketId);
}
================================================
FILE: src/ApplicationCore/Interfaces/IEmailSender.cs
================================================
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string message);
}
================================================
FILE: src/ApplicationCore/Interfaces/IOrderService.cs
================================================
using System.Threading.Tasks;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface IOrderService
{
Task CreateOrderAsync(int basketId, Address shippingAddress);
}
================================================
FILE: src/ApplicationCore/Interfaces/IReadRepository.cs
================================================
using Ardalis.Specification;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class, IAggregateRoot
{
}
================================================
FILE: src/ApplicationCore/Interfaces/IRepository.cs
================================================
using Ardalis.Specification;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
{
}
================================================
FILE: src/ApplicationCore/Interfaces/ITokenClaimsService.cs
================================================
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface ITokenClaimsService
{
Task<string> GetTokenAsync(string userName);
}
================================================
FILE: src/ApplicationCore/Interfaces/IUriComposer.cs
================================================
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;
public interface IUriComposer
{
string ComposePicUri(string uriTemplate);
}
================================================
FILE: src/ApplicationCore/Services/BasketService.cs
================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using Ardalis.Result;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
namespace Microsoft.eShopWeb.ApplicationCore.Services;
public class BasketService : IBasketService
{
private readonly IRepository<Basket> _basketRepository;
private readonly IAppLogger<BasketService> _logger;
public BasketService(IRepository<Basket> basketRepository,
IAppLogger<BasketService> logger)
{
_basketRepository = basketRepository;
_logger = logger;
}
public async Task<Basket> AddItemToBasket(string username, int catalogItemId, decimal price, int quantity = 1)
{
var basketSpec = new BasketWithItemsSpecification(username);
var basket = await _basketRepository.FirstOrDefaultAsync(basketSpec);
if (basket == null)
{
basket = new Basket(username);
await _basketRepository.AddAsync(basket);
}
basket.AddItem(catalogItemId, price, quantity);
await _basketRepository.UpdateAsync(basket);
return basket;
}
public async Task DeleteBasketAsync(int basketId)
{
var basket = await _basketRepository.GetByIdAsync(basketId);
Guard.Against.Null(basket, nameof(basket));
await _basketRepository.DeleteAsync(basket);
}
public async Task<Result<Basket>> SetQuantities(int basketId, Dictionary<string, int> quantities)
{
var basketSpec = new BasketWithItemsSpecification(basketId);
var basket = await _basketRepository.FirstOrDefaultAsync(basketSpec);
if (basket == null) return Result<Basket>.NotFound();
foreach (var item in basket.Items)
{
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);
}
}
basket.RemoveEmptyItems();
await _basketRepository.UpdateAsync(basket);
return basket;
}
public async Task TransferBasketAsync(string anonymousId, string userName)
{
var anonymousBasketSpec = new BasketWithItemsSpecification(anonymousId);
var anonymousBasket = await _basketRepository.FirstOrDefaultAsync(anonymousBasketSpec);
if (anonymousBasket == null) return;
var userBasketSpec = new BasketWithItemsSpecification(userName);
var userBasket = await _basketRepository.FirstOrDefaultAsync(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);
}
}
================================================
FILE: src/ApplicationCore/Services/OrderService.cs
================================================
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;
namespace Microsoft.eShopWeb.ApplicationCore.Services;
public class OrderService : IOrderService
{
private readonly IRepository<Order> _orderRepository;
private readonly IUriComposer _uriComposer;
private readonly IRepository<Basket> _basketRepository;
private readonly IRepository<CatalogItem> _itemRepository;
public OrderService(IRepository<Basket> basketRepository,
IRepository<CatalogItem> itemRepository,
IRepository<Order> orderRepository,
IUriComposer uriComposer)
{
_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.FirstOrDefaultAsync(basketSpec);
Guard.Against.Null(basket, nameof(basket));
Guard.Against.EmptyBasketOnCheckout(basket.Items);
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 order = new Order(basket.BuyerId, shippingAddress, items);
await _orderRepository.AddAsync(order);
}
}
================================================
FILE: src/ApplicationCore/Services/UriComposer.cs
================================================
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
namespace Microsoft.eShopWeb.ApplicationCore.Services;
public class UriComposer : IUriComposer
{
private readonly CatalogSettings _catalogSettings;
public UriComposer(CatalogSettings catalogSettings) => _catalogSettings = catalogSettings;
public string ComposePicUri(string uriTemplate)
{
return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl);
}
}
================================================
FILE: src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs
================================================
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public sealed class BasketWithItemsSpecification : Specification<Basket>
{
public BasketWithItemsSpecification(int basketId)
{
Query
.Where(b => b.Id == basketId)
.Include(b => b.Items);
}
public BasketWithItemsSpecification(string buyerId)
{
Query
.Where(b => b.BuyerId == buyerId)
.Include(b => b.Items);
}
}
================================================
FILE: src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs
================================================
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public class CatalogFilterPaginatedSpecification : Specification<CatalogItem>
{
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
: base()
{
if (take == 0)
{
take = int.MaxValue;
}
Query
.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
(!typeId.HasValue || i.CatalogTypeId == typeId))
.Skip(skip).Take(take);
}
}
================================================
FILE: src/ApplicationCore/Specifications/CatalogFilterSpecification.cs
================================================
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public class CatalogFilterSpecification : Specification<CatalogItem>
{
public CatalogFilterSpecification(int? brandId, int? typeId)
{
Query.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
(!typeId.HasValue || i.CatalogTypeId == typeId));
}
}
================================================
FILE: src/ApplicationCore/Specifications/CatalogItemNameSpecification.cs
================================================
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public class CatalogItemNameSpecification : Specification<CatalogItem>
{
public CatalogItemNameSpecification(string catalogItemName)
{
Query.Where(item => catalogItemName == item.Name);
}
}
================================================
FILE: src/ApplicationCore/Specifications/CatalogItemsSpecification.cs
================================================
using System;
using System.Linq;
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public class CatalogItemsSpecification : Specification<CatalogItem>
{
public CatalogItemsSpecification(params int[] ids)
{
Query.Where(c => ids.Contains(c.Id));
}
}
================================================
FILE: src/ApplicationCore/Specifications/CustomerOrdersSpecification.cs
================================================
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public class CustomerOrdersSpecification : Specification<Order>
{
public CustomerOrdersSpecification(string buyerId)
{
Query.Where(o => o.BuyerId == buyerId)
.Include(o => o.OrderItems);
}
}
================================================
FILE: src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs
================================================
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public class CustomerOrdersWithItemsSpecification : Specification<Order>
{
public CustomerOrdersWithItemsSpecification(string buyerId)
{
Query.Where(o => o.BuyerId == buyerId)
.Include(o => o.OrderItems)
.ThenInclude(i => i.ItemOrdered);
}
}
================================================
FILE: src/ApplicationCore/Specifications/OrderWithItemsByIdSpec.cs
================================================
using Ardalis.Specification;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
public class OrderWithItemsByIdSpec : Specification<Order>
{
public OrderWithItemsByIdSpec(int orderId)
{
Query
.Where(order => order.Id == orderId)
.Include(o => o.OrderItems)
.ThenInclude(i => i.ItemOrdered);
}
}
================================================
FILE: src/BlazorAdmin/App.razor
================================================
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@if (!context.User.Identity.IsAuthenticated)
{
<RedirectToLogin />
}
else
{
<h2>Not Authorized</h2>
<p>
You are not authorized to access
this resource.
<a href="/">Return to eShop</a>
</p>
}
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
================================================
FILE: src/BlazorAdmin/BlazorAdmin.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" />
<PackageReference Include="BlazorInputFile" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" />
<PackageReference Include="System.Net.Http.Json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BlazorShared\BlazorShared.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Services\CatalogItem\Delete.EditCatalogItemResult.cs">
<DependentUpon>Delete.cs</DependentUpon>
</Compile>
<Compile Update="Services\CatalogItem\GetById.EditCatalogItemResult.cs">
<DependentUpon>GetById.cs</DependentUpon>
</Compile>
<Compile Update="Services\CatalogItem\Edit.CreateCatalogItemResult.cs">
<DependentUpon>Edit.cs</DependentUpon>
</Compile>
</ItemGroup>
</Project>
================================================
FILE: src/BlazorAdmin/CustomAuthStateProvider.cs
================================================
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;
public class CustomAuthStateProvider : AuthenticationStateProvider
{
// TODO: Get Default Cache Duration from Config
private static readonly TimeSpan UserCacheRefreshInterval = TimeSpan.FromSeconds(60);
private readonly HttpClient _httpClient;
private readonly ILogger<CustomAuthStateProvider> _logger;
private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
public CustomAuthStateProvider(HttpClient httpClient,
ILogger<CustomAuthStateProvider> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
return new AuthenticationState(await GetUser(useCache: true));
}
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
{
var now = DateTimeOffset.Now;
if (useCache && now < _userLastCheck + UserCacheRefreshInterval)
{
return _cachedUser;
}
_cachedUser = await FetchUser();
_userLastCheck = now;
return _cachedUser;
}
private async Task<ClaimsPrincipal> FetchUser()
{
UserInfo user = null;
try
{
_logger.LogInformation("Fetching user details from web api.");
user = await _httpClient.GetFromJsonAsync<UserInfo>("User");
}
catch (Exception exc)
{
_logger.LogWarning(exc, "Fetching user failed.");
}
if (user == null || !user.IsAuthenticated)
{
return new ClaimsPrincipal(new ClaimsIdentity());
}
var identity = new ClaimsIdentity(
nameof(CustomAuthStateProvider),
user.NameClaimType,
user.RoleClaimType);
if (user.Claims != null)
{
foreach (var claim in user.Claims)
{
identity.AddClaim(new Claim(claim.Type, claim.Value));
}
}
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
return new ClaimsPrincipal(identity);
}
}
================================================
FILE: src/BlazorAdmin/Helpers/BlazorComponent.cs
================================================
using Microsoft.AspNetCore.Components;
namespace BlazorAdmin.Helpers;
public class BlazorComponent : ComponentBase
{
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
protected override void OnInitialized()
{
_refresh.RefreshRequested += DoRefresh;
base.OnInitialized();
}
public void CallRequestRefresh()
{
_refresh.CallRequestRefresh();
}
private void DoRefresh()
{
StateHasChanged();
}
}
================================================
FILE: src/BlazorAdmin/Helpers/BlazorLayoutComponent.cs
================================================
using Microsoft.AspNetCore.Components;
namespace BlazorAdmin.Helpers;
public class BlazorLayoutComponent : LayoutComponentBase
{
private readonly RefreshBroadcast _refresh = RefreshBroadcast.Instance;
protected override void OnInitialized()
{
_refresh.RefreshRequested += DoRefresh;
base.OnInitialized();
}
public void CallRequestRefresh()
{
_refresh.CallRequestRefresh();
}
private void DoRefresh()
{
StateHasChanged();
}
}
================================================
FILE: src/BlazorAdmin/Helpers/RefreshBroadcast.cs
================================================
using System;
namespace BlazorAdmin.Helpers;
internal sealed class RefreshBroadcast
{
private static readonly Lazy<RefreshBroadcast>
Lazy =
new Lazy<RefreshBroadcast>
(() => new RefreshBroadcast());
public static RefreshBroadcast Instance => Lazy.Value;
private RefreshBroadcast()
{
}
public event Action RefreshRequested;
public void CallRequestRefresh()
{
RefreshRequested?.Invoke();
}
}
================================================
FILE: src/BlazorAdmin/Helpers/ToastComponent.cs
================================================
using System;
using BlazorAdmin.Services;
using Microsoft.AspNetCore.Components;
namespace BlazorAdmin.Helpers;
public class ToastComponent : ComponentBase, IDisposable
{
[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()
{
ToastService.OnShow -= ShowToast;
}
}
================================================
FILE: src/BlazorAdmin/JavaScript/Cookies.cs
================================================
using System.Threading.Tasks;
using Microsoft.JSInterop;
namespace BlazorAdmin.JavaScript;
public class Cookies
{
private readonly IJSRuntime _jsRuntime;
public Cookies(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task DeleteCookie(string name)
{
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.DeleteCookie, name);
}
public async Task<string> GetCookie(string name)
{
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.GetCookie, name);
}
}
================================================
FILE: src/BlazorAdmin/JavaScript/Css.cs
================================================
using System.Threading.Tasks;
using Microsoft.JSInterop;
namespace BlazorAdmin.JavaScript;
public class Css
{
private readonly IJSRuntime _jsRuntime;
public Css(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task ShowBodyOverflow()
{
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.ShowBodyOverflow);
}
public async Task<string> HideBodyOverflow()
{
return await _jsRuntime.InvokeAsync<string>(JSInteropConstants.HideBodyOverflow);
}
}
================================================
FILE: src/BlazorAdmin/JavaScript/JSInteropConstants.cs
================================================
namespace BlazorAdmin.JavaScript;
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";
}
================================================
FILE: src/BlazorAdmin/JavaScript/Route.cs
================================================
using System.Threading.Tasks;
using Microsoft.JSInterop;
namespace BlazorAdmin.JavaScript;
public class Route
{
private readonly IJSRuntime _jsRuntime;
public Route(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task RouteOutside(string path)
{
await _jsRuntime.InvokeAsync<string>(JSInteropConstants.RouteOutside, path);
}
}
================================================
FILE: src/BlazorAdmin/Pages/CatalogItemPage/Create.razor
================================================
@inject ILogger<Create> Logger
@inject IJSRuntime JSRuntime
@inject ICatalogItemService CatalogItemService
@inherits BlazorAdmin.Helpers.BlazorComponent
@namespace BlazorAdmin.Pages.CatalogItemPage
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay">
<div class="modal-dialog" role="document">
<div class="modal-content">
<EditForm Model="_item" OnValidSubmit="@CreateClick">
<DataAnnotationsValidator />
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Create</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
@if (_item == null)
{
<Spinner></Spinner>
}
else
{
<div class="container">
<div class="row">
@if (HasPicture)
{
<img class="col-md-6 esh-picture" src="@LoadPicture">
}
<div class="col-md-@(HasPicture?"6":"12")">
<div class="form-group">
<label class="control-label col-md-6">Name</label>
<div class="col-md-12">
<InputText class="form-control" @bind-Value="_item.Name" />
<ValidationMessage For="(() => _item.Name)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">Description</label>
<div class="col-md-12">
<InputText class="form-control" @bind-Value="_item.Description" />
<ValidationMessage For="(() => _item.Description)" />
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label class="control-label col-md-6">Brand</label>
<div class="col-md-12">
<InputSelect @bind-Value="_item.CatalogBrandId" class="form-control">
@foreach (var brand in Brands)
{
<option value="@brand.Id">@brand.Name</option>
}
</InputSelect>
<ValidationMessage For="(() => _item.CatalogBrandId)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">Type</label>
<div class="col-md-12">
<InputSelect @bind-Value="_item.CatalogTypeId" class="form-control">
@foreach (var type in Types)
{
<option value="@type.Id">@type.Name</option>
}
</InputSelect>
<ValidationMessage For="(() => _item.CatalogTypeId)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">Price</label>
<div class="col-md-12">
<InputNumber @bind-Value="_item.Price" class="form-control" />
<ValidationMessage For="(() => _item.Price)" />
</div>
</div>
</div>
</div>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button>
<button type="submit" class="btn btn-primary">
Create
</button>
</div>
</EditForm>
</div>
</div>
</div>
@if (_showCreateModal)
{
<div class="modal-backdrop fade show"></div>
}
@code {
[Parameter]
public IEnumerable<CatalogBrand> Brands { get; set; }
[Parameter]
public IEnumerable<CatalogType> Types { get; set; }
[Parameter]
public EventCallback<string> OnSaveClick { get; set; }
private string LoadPicture => string.IsNullOrEmpty(_item.PictureBase64) ? string.Empty : $"data:image/png;base64, {_item.PictureBase64}";
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureBase64);
private string _badFileMessage = string.Empty;
private string _modalDisplay = "none;";
private string _modalClass = "";
private bool _showCreateModal = false;
private CreateCatalogItemRequest _item = new CreateCatalogItemRequest();
private async Task CreateClick()
{
var result = await CatalogItemService.Create(_item);
if (result != null)
{
await OnSaveClick.InvokeAsync(null);
await Close();
}
}
public async Task Open()
{
Logger.LogInformation("Now loading... /Catalog/Create");
await new Css(JSRuntime).HideBodyOverflow();
_item = new CreateCatalogItemRequest
{
CatalogTypeId = Types.First().Id,
CatalogBrandId = Brands.First().Id
};
_modalDisplay = "block;";
_modalClass = "Show";
_showCreateModal = true;
StateHasChanged();
}
private async Task Close()
{
await new Css(JSRuntime).ShowBodyOverflow();
_modalDisplay = "none";
_modalClass = "";
_showCreateModal = false;
}
}
================================================
FILE: src/BlazorAdmin/Pages/CatalogItemPage/Delete.razor
================================================
@inject ILogger<Delete> Logger
@inject IJSRuntime JSRuntime
@inject ICatalogItemService CatalogItemService
@inherits BlazorAdmin.Helpers.BlazorComponent
@namespace BlazorAdmin.Pages.CatalogItemPage
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Delete @_item.Name</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
@if (_item == null)
{
<Spinner></Spinner>
}
else
{
<div class="container">
<div class="row">
@if (HasPicture)
{
<img class="col-md-6 esh-picture" src="@($"{_item.PictureUri}")">
}
<dl class="col-md-@(HasPicture ? "6" : "12") dl-horizontal">
<dt>
Name
</dt>
<dd>
@_item.Name
</dd>
<dt>
Description
</dt>
<dd>
@_item.Description
</dd>
<dt>
Brand
</dt>
<dd>
@_item.CatalogBrand
</dd>
<dt>
Type
</dt>
<dd>
@_item.CatalogType
</dd>
<dt>
Price
</dt>
<dd>
@_item.Price
</dd>
</dl>
</div>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button>
<button class="btn btn-danger" @onclick="() => DeleteClick(_item.Id)">
Delete
</button>
</div>
</div>
</div>
</div>
@if (_showDeleteModal)
{
<div class="modal-backdrop fade show"></div>
}
@code {
[Parameter]
public IEnumerable<CatalogBrand> Brands { get; set; }
[Parameter]
public IEnumerable<CatalogType> Types { get; set; }
[Parameter]
public EventCallback<string> OnSaveClick { get; set; }
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureUri);
private string _modalDisplay = "none;";
private string _modalClass = "";
private bool _showDeleteModal = false;
private CatalogItem _item = new CatalogItem();
private async Task DeleteClick(int id)
{
// TODO: Add some kind of "are you sure" check before this
await CatalogItemService.Delete(id);
await OnSaveClick.InvokeAsync(null);
await Close();
}
public async Task Open(int id)
{
Logger.LogInformation("Now loading... /Catalog/Delete/{Id}", id);
await new Css(JSRuntime).HideBodyOverflow();
_item = await CatalogItemService.GetById(id);
_modalDisplay = "block;";
_modalClass = "Show";
_showDeleteModal = true;
StateHasChanged();
}
private async Task Close()
{
await new Css(JSRuntime).ShowBodyOverflow();
_modalDisplay = "none";
_modalClass = "";
_showDeleteModal = false;
}
}
================================================
FILE: src/BlazorAdmin/Pages/CatalogItemPage/Details.razor
================================================
@inject ILogger<Details> Logger
@inject IJSRuntime JSRuntime
@inject ICatalogItemService CatalogItemService
@inherits BlazorAdmin.Helpers.BlazorComponent
@namespace BlazorAdmin.Pages.CatalogItemPage
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Details @_item.Name</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
@if (_item == null)
{
<Spinner></Spinner>
}
else
{
<div class="container">
<div class="row">
@if (HasPicture)
{
<img class="col-md-6 esh-picture" src="@($"{_item.PictureUri}")">
}
<dl class="col-md-@(HasPicture?"6":"12") dl-horizontal">
<dt>
Name
</dt>
<dd>
@_item.Name
</dd>
<dt>
Description
</dt>
<dd>
@_item.Description
</dd>
<dt>
Brand
</dt>
<dd>
@_item.CatalogBrand
</dd>
<dt>
Type
</dt>
<dd>
@_item.CatalogType
</dd>
<dt>
Price
</dt>
<dd>
@_item.Price
</dd>
</dl>
</div>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Close</button>
<button class="btn btn-primary" @onclick="EditClick">
Edit
</button>
</div>
</div>
</div>
</div>
@if (_showDetailsModal)
{
<div class="modal-backdrop fade show"></div>
}
@code {
[Parameter]
public IEnumerable<CatalogBrand> Brands { get; set; }
[Parameter]
public IEnumerable<CatalogType> Types { get; set; }
[Parameter]
public EventCallback<int> OnEditClick { get; set; }
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureUri);
private string _modalDisplay = "none;";
private string _modalClass = "";
private bool _showDetailsModal = false;
private CatalogItem _item = new CatalogItem();
public async Task EditClick()
{
await OnEditClick.InvokeAsync(_item.Id);
await Close();
}
public async Task Open(int id)
{
Logger.LogInformation("Now loading... /Catalog/Details/{Id}", id);
await new Css(JSRuntime).HideBodyOverflow();
_item = await CatalogItemService.GetById(id);
_modalDisplay = "block;";
_modalClass = "Show";
_showDetailsModal = true;
StateHasChanged();
}
public async Task Close()
{
await new Css(JSRuntime).ShowBodyOverflow();
_modalDisplay = "none";
_modalClass = "";
_showDetailsModal = false;
}
}
================================================
FILE: src/BlazorAdmin/Pages/CatalogItemPage/Edit.razor
================================================
@inject ILogger<Edit> Logger
@inject IJSRuntime JSRuntime
@inject ICatalogItemService CatalogItemService
@inherits BlazorAdmin.Helpers.BlazorComponent
@namespace BlazorAdmin.Pages.CatalogItemPage
<div class="modal @_modalClass" tabindex="-1" role="dialog" style="display:@_modalDisplay">
<div class="modal-dialog" role="document">
<div class="modal-content">
<EditForm Model="_item" OnValidSubmit="@SaveClick">
<DataAnnotationsValidator />
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Edit @_item.Name</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
@if (_item == null)
{
<Spinner></Spinner>
}
else
{
<div class="container">
<div class="row">
@if (HasPicture)
{
<img class="col-md-6 esh-picture" src="@LoadPicture">
}
<div class="col-md-@(HasPicture?"6":"12") ">
<div class="form-group">
<label class="control-label col-md-6">Name</label>
<div class="col-md-12">
<InputText class="form-control" @bind-Value="_item.Name" />
<ValidationMessage For="(() => _item.Name)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">Description</label>
<div class="col-md-12">
<InputText class="form-control" @bind-Value="_item.Description" />
<ValidationMessage For="(() => _item.Description)" />
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label class="control-label col-md-6">Brand</label>
<div class="col-md-12">
<CustomInputSelect @bind-Value="_item.CatalogBrandId" class="form-control">
@foreach (var brand in Brands)
{
<option value="@brand.Id.ToString()">@brand.Name</option>
}
</CustomInputSelect>
<ValidationMessage For="(() => _item.CatalogBrandId)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">Type</label>
<div class="col-md-12">
<CustomInputSelect @bind-Value="_item.CatalogTypeId" class="form-control">
@foreach (var type in Types)
{
<option value="@type.Id">@type.Name</option>
}
</CustomInputSelect>
<ValidationMessage For="(() => _item.CatalogTypeId)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">Price</label>
<div class="col-md-12">
<InputNumber @bind-Value="_item.Price" class="form-control" />
<ValidationMessage For="(() => _item.Price)" />
</div>
</div>
</div>
</div>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="Close">Cancel</button>
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</EditForm>
</div>
</div>
</div>
@if (_showEditModal)
{
<div class="modal-backdrop fade show"></div>
}
@code {
[Parameter]
public IEnumerable<CatalogBrand> Brands { get; set; }
[Parameter]
public IEnumerable<CatalogType> Types { get; set; }
[Parameter]
public EventCallback<string> OnSaveClick { get; set; }
private string LoadPicture => string.IsNullOrEmpty(_item.PictureBase64) ? string.IsNullOrEmpty(_item.PictureUri) ? string.Empty : $"{_item.PictureUri}" : $"data:image/png;base64, {_item.PictureBase64}";
private bool HasPicture => !(string.IsNullOrEmpty(_item.PictureBase64) && string.IsNullOrEmpty(_item.PictureUri));
private string _badFileMessage = string.Empty;
private string _modalDisplay = "none;";
private string _modalClass = "";
private bool _showEditModal = false;
private CatalogItem _item = new CatalogItem();
private async Task SaveClick()
{
await CatalogItemService.Edit(_item);
await OnSaveClick.InvokeAsync(null);
await Close();
}
public async Task Open(int id)
{
Logger.LogInformation("Now loading... /Catalog/Edit/{Id}", id);
await new Css(JSRuntime).HideBodyOverflow();
_item = await CatalogItemService.GetById(id);
_modalDisplay = "block;";
_modalClass = "Show";
_showEditModal = true;
StateHasChanged();
}
private async Task Close()
{
await new Css(JSRuntime).ShowBodyOverflow();
_modalDisplay = "none";
_modalClass = "";
_showEditModal = false;
}
}
================================================
FILE: src/BlazorAdmin/Pages/CatalogItemPage/List.razor
================================================
@page "/admin"
@attribute [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)]
@inherits BlazorAdmin.Helpers.BlazorComponent
@namespace BlazorAdmin.Pages.CatalogItemPage
<PageTitle>eShopOnWeb Admin: Manage Product Catalog</PageTitle>
<h1>Manage Product Catalog</h1>
@if (catalogItems == null)
{
<Spinner></Spinner>
}
else
{
<p class="esh-link-wrapper">
<button class="btn btn-primary" @onclick="@(CreateClick)">
Create New
</button>
</p>
<table class="table table-striped table-hover">
<thead>
<tr>
<th></th>
<th>Item Type</th>
<th>Brand</th>
<th>Id</th>
<th>Name</th>
<th>@nameof(CatalogItem.Description)</th>
<th>@nameof(CatalogItem.Price)</th>
<th>Actions</th>
</tr>
</thead>
<tbody class="cursor-pointer">
@foreach (var item in catalogItems)
{
<tr @onclick="@(() => DetailsClick(item.Id))">
<td>
<img class="img-thumbnail" src="@($"{item.PictureUri}")">
</td>
<td>@item.CatalogType</td>
<td>@item.CatalogBrand</td>
<td>@item.Id</td>
<td>@item.Name</td>
<td>@item.Description</td>
<td>@item.Price</td>
<td>
<button @onclick="@(() => EditClick(item.Id))" @onclick:stopPropagation="true" class="btn btn-primary">
Edit
</button>
<button @onclick="@(() => DeleteClick(item.Id))" @onclick:stopPropagation="true" class="btn btn-danger">
Delete
</button>
</td>
</tr>
}
</tbody>
</table>
<Details Brands="@catalogBrands" Types="@catalogTypes" OnEditClick="EditClick" @ref="DetailsComponent"></Details>
<Edit Brands="@catalogBrands" Types="@catalogTypes" OnSaveClick="ReloadCatalogItems" @ref="EditComponent"></Edit>
<Create Brands="@catalogBrands" Types="@catalogTypes" OnSaveClick="ReloadCatalogItems" @ref="CreateComponent"></Create>
<Delete Brands="@catalogBrands" Types="@catalogTypes" OnSaveClick="ReloadCatalogItems" @ref="DeleteComponent"></Delete>
}
================================================
FILE: src/BlazorAdmin/Pages/CatalogItemPage/List.razor.cs
================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using BlazorAdmin.Helpers;
using BlazorShared.Interfaces;
using BlazorShared.Models;
namespace BlazorAdmin.Pages.CatalogItemPage;
public partial class List : BlazorComponent
{
[Microsoft.AspNetCore.Components.Inject]
public ICatalogItemService CatalogItemService { get; set; }
[Microsoft.AspNetCore.Components.Inject]
public ICatalogLookupDataService<CatalogBrand> CatalogBrandService { get; set; }
[Microsoft.AspNetCore.Components.Inject]
public ICatalogLookupDataService<CatalogType> CatalogTypeService { get; set; }
private List<CatalogItem> catalogItems = new List<CatalogItem>();
private List<CatalogType> catalogTypes = new List<CatalogType>();
private List<CatalogBrand> catalogBrands = new List<CatalogBrand>();
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)
{
if (firstRender)
{
catalogItems = await CatalogItemService.List();
catalogTypes = await CatalogTypeService.List();
catalogBrands = await CatalogBrandService.List();
CallRequestRefresh();
}
await base.OnAfterRenderAsync(firstRender);
}
private async void DetailsClick(int id)
{
await DetailsComponent.Open(id);
}
private async Task CreateClick()
{
await CreateComponent.Open();
}
private async Task EditClick(int id)
{
await EditComponent.Open(id);
}
private async Task DeleteClick(int id)
{
await DeleteComponent.Open(id);
}
private async Task ReloadCatalogItems()
{
catalogItems = await CatalogItemService.List();
StateHasChanged();
}
}
================================================
FILE: src/BlazorAdmin/Pages/Logout.razor
================================================
@page "/logout"
@inject IJSRuntime JSRuntime
@inject HttpClient HttpClient
@inherits BlazorAdmin.Helpers.BlazorComponent
@code {
protected override async Task OnInitializedAsync()
{
await HttpClient.PostAsync("User/Logout", null);
await new Route(JSRuntime).RouteOutside("/Identity/Account/Login");
}
}
================================================
FILE: src/BlazorAdmin/Program.cs
================================================
using System;
using System.Net.Http;
using System.Threading.Tasks;
using BlazorAdmin;
using BlazorAdmin.Services;
using Blazored.LocalStorage;
using BlazorShared;
using BlazorShared.Models;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#admin");
builder.RootComponents.Add<HeadOutlet>("head::after");
var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME);
builder.Services.Configure<BaseUrlConfiguration>(configSection);
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<ToastService>();
builder.Services.AddScoped<HttpService>();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
builder.Services.AddBlazorServices();
builder.Logging.AddConfiguration(builder.Configuration.GetRequiredSection("Logging"));
await ClearLocalStorageCache(builder.Services);
await builder.Build().RunAsync();
static async Task ClearLocalStorageCache(IServiceCollection services)
{
var sp = services.BuildServiceProvider();
var localStorageService = sp.GetRequiredService<ILocalStorageService>();
await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name);
await localStorageService.RemoveItemAsync(typeof(CatalogType).Name);
}
================================================
FILE: src/BlazorAdmin/Properties/launchSettings.json
================================================
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:58126",
"sslPort": 44315
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"BlazorAdmin": {
"commandName": "Project",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
================================================
FILE: src/BlazorAdmin/Services/CacheEntry.cs
================================================
using System;
namespace BlazorAdmin.Services;
public class CacheEntry<T>
{
public CacheEntry(T item)
{
Value = item;
}
public CacheEntry()
{
}
public T Value { get; set; }
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
}
================================================
FILE: src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs
================================================
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;
public class CachedCatalogItemServiceDecorator : ICatalogItemService
{
private readonly ILocalStorageService _localStorageService;
private readonly CatalogItemService _catalogItemService;
private ILogger<CachedCatalogItemServiceDecorator> _logger;
public CachedCatalogItemServiceDecorator(ILocalStorageService localStorageService,
CatalogItemService catalogItemService,
ILogger<CachedCatalogItemServiceDecorator> logger)
{
_localStorageService = localStorageService;
_catalogItemService = catalogItemService;
_logger = logger;
}
public async Task<List<CatalogItem>> ListPaged(int pageSize)
{
string key = "items";
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
if (cacheEntry != null)
{
_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);
}
}
var items = await _catalogItemService.ListPaged(pageSize);
var entry = new CacheEntry<List<CatalogItem>>(items);
await _localStorageService.SetItemAsync(key, entry);
return items;
}
public async Task<List<CatalogItem>> List()
{
string key = "items";
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<CatalogItem>>>(key);
if (cacheEntry != null)
{
_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);
}
}
var items = await _catalogItemService.List();
var entry = new CacheEntry<List<CatalogItem>>(items);
await _localStorageService.SetItemAsync(key, entry);
return items;
}
public async Task<CatalogItem> GetById(int id)
{
return (await List()).FirstOrDefault(x => x.Id == id);
}
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
{
var result = await _catalogItemService.Create(catalogItem);
await RefreshLocalStorageList();
return result;
}
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
{
var result = await _catalogItemService.Edit(catalogItem);
await RefreshLocalStorageList();
return result;
}
public async Task<string> Delete(int id)
{
var result = await _catalogItemService.Delete(id);
await RefreshLocalStorageList();
return result;
}
private async Task RefreshLocalStorageList()
{
string key = "items";
await _localStorageService.RemoveItemAsync(key);
var items = await _catalogItemService.List();
var entry = new CacheEntry<List<CatalogItem>>(items);
await _localStorageService.SetItemAsync(key, entry);
}
}
================================================
FILE: src/BlazorAdmin/Services/CachedCatalogLookupDataServiceDecorator .cs
================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Blazored.LocalStorage;
using BlazorShared.Interfaces;
using BlazorShared.Models;
using Microsoft.Extensions.Logging;
namespace BlazorAdmin.Services;
public class CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>
: ICatalogLookupDataService<TLookupData>
where TLookupData : LookupData
where TReponse : ILookupDataResponse<TLookupData>
{
private readonly ILocalStorageService _localStorageService;
private readonly CatalogLookupDataService<TLookupData, TReponse> _catalogTypeService;
private ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> _logger;
public CachedCatalogLookupDataServiceDecorator(ILocalStorageService localStorageService,
CatalogLookupDataService<TLookupData, TReponse> catalogTypeService,
ILogger<CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>> logger)
{
_localStorageService = localStorageService;
_catalogTypeService = catalogTypeService;
_logger = logger;
}
public async Task<List<TLookupData>> List()
{
string key = typeof(TLookupData).Name;
var cacheEntry = await _localStorageService.GetItemAsync<CacheEntry<List<TLookupData>>>(key);
if (cacheEntry != null)
{
_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);
}
}
var types = await _catalogTypeService.List();
var entry = new CacheEntry<List<TLookupData>>(types);
await _localStorageService.SetItemAsync(key, entry);
return types;
}
}
================================================
FILE: src/BlazorAdmin/Services/CatalogItemService.cs
================================================
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BlazorShared.Interfaces;
using BlazorShared.Models;
using Microsoft.Extensions.Logging;
namespace BlazorAdmin.Services;
public class CatalogItemService : ICatalogItemService
{
private readonly ICatalogLookupDataService<CatalogBrand> _brandService;
private readonly ICatalogLookupDataService<CatalogType> _typeService;
private readonly HttpService _httpService;
private readonly ILogger<CatalogItemService> _logger;
public CatalogItemService(ICatalogLookupDataService<CatalogBrand> brandService,
ICatalogLookupDataService<CatalogType> typeService,
HttpService httpService,
ILogger<CatalogItemService> logger)
{
_brandService = brandService;
_typeService = typeService;
_httpService = httpService;
_logger = logger;
}
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
{
var response = await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem);
return response?.CatalogItem;
}
public async Task<CatalogItem> Edit(CatalogItem catalogItem)
{
return (await _httpService.HttpPut<EditCatalogItemResult>("catalog-items", catalogItem)).CatalogItem;
}
public async Task<string> Delete(int catalogItemId)
{
return (await _httpService.HttpDelete<DeleteCatalogItemResponse>("catalog-items", catalogItemId)).Status;
}
public async Task<CatalogItem> GetById(int id)
{
var brandListTask = _brandService.List();
var typeListTask = _typeService.List();
var itemGetTask = _httpService.HttpGet<EditCatalogItemResult>($"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<List<CatalogItem>> ListPaged(int pageSize)
{
_logger.LogInformation("Fetching catalog items from API.");
var brandListTask = _brandService.List();
var typeListTask = _typeService.List();
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"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;
}
public async Task<List<CatalogItem>> List()
{
_logger.LogInformation("Fetching catalog items from API.");
var brandListTask = _brandService.List();
var typeListTask = _typeService.List();
var itemListTask = _httpService.HttpGet<PagedCatalogItemResponse>($"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;
}
}
================================================
FILE: src/BlazorAdmin/Services/CatalogLookupDataService.cs
================================================
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;
using Microsoft.Extensions.Options;
namespace BlazorAdmin.Services;
public class CatalogLookupDataService<TLookupData, TReponse>
: ICatalogLookupDataService<TLookupData>
where TLookupData : LookupData
where TReponse : ILookupDataResponse<TLookupData>
{
private readonly HttpClient _httpClient;
private readonly ILogger<CatalogLookupDataService<TLookupData, TReponse>> _logger;
private readonly string _apiUrl;
public CatalogLookupDataService(HttpClient httpClient,
IOptions<BaseUrlConfiguration> baseUrlConfiguration,
ILogger<CatalogLookupDataService<TLookupData, TReponse>> logger)
{
_httpClient = httpClient;
_logger = logger;
_apiUrl = baseUrlConfiguration.Value.ApiBase;
}
public async Task<List<TLookupData>> List()
{
var endpointName = typeof(TLookupData).GetCustomAttribute<EndpointAttribute>().Name;
_logger.LogInformation($"Fetching {typeof(TLookupData).Name} from API. Enpoint : {endpointName}");
var response = await _httpClient.GetFromJsonAsync<TReponse>($"{_apiUrl}{endpointName}");
return response.List;
}
}
================================================
FILE: src/BlazorAdmin/Services/HttpService.cs
================================================
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using BlazorShared;
using BlazorShared.Models;
using Microsoft.Extensions.Options;
namespace BlazorAdmin.Services;
public class HttpService
{
private readonly HttpClient _httpClient;
private readonly ToastService _toastService;
private readonly string _apiUrl;
public HttpService(HttpClient httpClient, IOptions<BaseUrlConfiguration> baseUrlConfiguration, ToastService toastService)
{
_httpClient = httpClient;
_toastService = toastService;
_apiUrl = baseUrlConfiguration.Value.ApiBase;
}
public async Task<T> HttpGet<T>(string uri)
where T : class
{
var result = await _httpClient.GetAsync($"{_apiUrl}{uri}");
if (!result.IsSuccessStatusCode)
{
return null;
}
return await FromHttpResponseMessage<T>(result);
}
public async Task<T> HttpDelete<T>(string uri, int id)
where T : class
{
var result = await _httpClient.DeleteAsync($"{_apiUrl}{uri}/{id}");
if (!result.IsSuccessStatusCode)
{
return null;
}
return await FromHttpResponseMessage<T>(result);
}
public async Task<T> HttpPost<T>(string uri, object dataToSend)
where T : class
{
var content = ToJson(dataToSend);
var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content);
if (!result.IsSuccessStatusCode)
{
var exception = JsonSerializer.Deserialize<ErrorDetails>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
_toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error);
return null;
}
return await FromHttpResponseMessage<T>(result);
}
public async Task<T> HttpPut<T>(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<T>(result);
}
private StringContent ToJson(object obj)
{
return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json");
}
private async Task<T> FromHttpResponseMessage<T>(HttpResponseMessage result)
{
return JsonSerializer.Deserialize<T>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
}
}
================================================
FILE: src/BlazorAdmin/Services/ToastService.cs
================================================
using System;
using System.Timers;
namespace BlazorAdmin.Services;
public enum ToastLevel
{
Info,
Success,
Warning,
Error
}
public class ToastService : IDisposable
{
public event Action<string, ToastLevel> 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)
{
Countdown.Stop();
Countdown.Start();
}
else
{
Countdown.Start();
}
}
private void SetCountdown()
{
if (Countdown == null)
{
Countdown = new Timer(3000);
Countdown.Elapsed += HideToast;
Countdown.AutoReset = false;
}
}
private void HideToast(object source, ElapsedEventArgs args)
{
OnHide?.Invoke();
}
public void Dispose()
{
gitextract_58gu5nma/
├── .ado/
│ ├── eshoponweb-cd-aci.yml
│ ├── eshoponweb-cd-webapp-code.yml
│ ├── eshoponweb-cd-webapp-docker.yml
│ ├── eshoponweb-cd-webapp-dockercompose.yml
│ ├── eshoponweb-cd-windows-cm.yml
│ ├── eshoponweb-ci-docker.yml
│ ├── eshoponweb-ci-dockercompose.yml
│ ├── eshoponweb-ci-mend.yml
│ ├── eshoponweb-ci-pr.yml
│ ├── eshoponweb-ci.yml
│ └── eshoponweb-sonar-ci.yml
├── .dockerignore
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── dotnetcore.yml
│ ├── eshoponweb-cicd.yml
│ └── richnav.yml
├── .gitignore
├── .vscode/
│ ├── extensions.json
│ ├── launch.json
│ └── tasks.json
├── CodeCoverage.runsettings
├── Directory.Packages.props
├── LICENSE
├── MTT-Notes.md
├── README.md
├── azure.yaml
├── docker-compose-webapp.yml
├── docker-compose.dcproj
├── docker-compose.override.yml
├── docker-compose.yml
├── eShopOnWeb.sln
├── global.json
├── infra/
│ ├── abbreviations.json
│ ├── aci.bicep
│ ├── acr.bicep
│ ├── core/
│ │ ├── database/
│ │ │ └── sqlserver/
│ │ │ └── sqlserver.bicep
│ │ ├── host/
│ │ │ ├── appservice.bicep
│ │ │ └── appserviceplan.bicep
│ │ └── security/
│ │ ├── keyvault-access.bicep
│ │ └── keyvault.bicep
│ ├── main.bicep
│ ├── main.parameters.json
│ ├── simple-windows-vm.bicep
│ ├── webapp-docker.bicep
│ ├── webapp-to-acr-roleassignment.bicep
│ └── webapp.bicep
├── src/
│ ├── ApplicationCore/
│ │ ├── ApplicationCore.csproj
│ │ ├── CatalogSettings.cs
│ │ ├── Constants/
│ │ │ └── AuthorizationConstants.cs
│ │ ├── Entities/
│ │ │ ├── BaseEntity.cs
│ │ │ ├── BasketAggregate/
│ │ │ │ ├── Basket.cs
│ │ │ │ └── BasketItem.cs
│ │ │ ├── BuyerAggregate/
│ │ │ │ ├── Buyer.cs
│ │ │ │ └── PaymentMethod.cs
│ │ │ ├── CatalogBrand.cs
│ │ │ ├── CatalogItem.cs
│ │ │ ├── CatalogType.cs
│ │ │ ├── EshopDiagram.cd
│ │ │ └── OrderAggregate/
│ │ │ ├── Address.cs
│ │ │ ├── CatalogItemOrdered.cs
│ │ │ ├── Order.cs
│ │ │ └── OrderItem.cs
│ │ ├── Exceptions/
│ │ │ ├── BasketNotFoundException.cs
│ │ │ ├── DuplicateException.cs
│ │ │ └── EmptyBasketOnCheckoutException.cs
│ │ ├── Extensions/
│ │ │ ├── GuardExtensions.cs
│ │ │ └── JsonExtensions.cs
│ │ ├── Interfaces/
│ │ │ ├── IAggregateRoot.cs
│ │ │ ├── IAppLogger.cs
│ │ │ ├── IBasketQueryService.cs
│ │ │ ├── IBasketService.cs
│ │ │ ├── IEmailSender.cs
│ │ │ ├── IOrderService.cs
│ │ │ ├── IReadRepository.cs
│ │ │ ├── IRepository.cs
│ │ │ ├── ITokenClaimsService.cs
│ │ │ └── IUriComposer.cs
│ │ ├── Services/
│ │ │ ├── BasketService.cs
│ │ │ ├── OrderService.cs
│ │ │ └── UriComposer.cs
│ │ └── Specifications/
│ │ ├── BasketWithItemsSpecification.cs
│ │ ├── CatalogFilterPaginatedSpecification.cs
│ │ ├── CatalogFilterSpecification.cs
│ │ ├── CatalogItemNameSpecification.cs
│ │ ├── CatalogItemsSpecification.cs
│ │ ├── CustomerOrdersSpecification.cs
│ │ ├── CustomerOrdersWithItemsSpecification.cs
│ │ └── OrderWithItemsByIdSpec.cs
│ ├── BlazorAdmin/
│ │ ├── App.razor
│ │ ├── BlazorAdmin.csproj
│ │ ├── CustomAuthStateProvider.cs
│ │ ├── Helpers/
│ │ │ ├── BlazorComponent.cs
│ │ │ ├── BlazorLayoutComponent.cs
│ │ │ ├── RefreshBroadcast.cs
│ │ │ └── ToastComponent.cs
│ │ ├── JavaScript/
│ │ │ ├── Cookies.cs
│ │ │ ├── Css.cs
│ │ │ ├── JSInteropConstants.cs
│ │ │ └── Route.cs
│ │ ├── Pages/
│ │ │ ├── CatalogItemPage/
│ │ │ │ ├── Create.razor
│ │ │ │ ├── Delete.razor
│ │ │ │ ├── Details.razor
│ │ │ │ ├── Edit.razor
│ │ │ │ ├── List.razor
│ │ │ │ └── List.razor.cs
│ │ │ └── Logout.razor
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── launchSettings.json
│ │ ├── Services/
│ │ │ ├── CacheEntry.cs
│ │ │ ├── CachedCatalogItemServiceDecorator.cs
│ │ │ ├── CachedCatalogLookupDataServiceDecorator .cs
│ │ │ ├── CatalogItemService.cs
│ │ │ ├── CatalogLookupDataService.cs
│ │ │ ├── HttpService.cs
│ │ │ └── ToastService.cs
│ │ ├── ServicesConfiguration.cs
│ │ ├── Shared/
│ │ │ ├── CustomInputSelect.cs
│ │ │ ├── MainLayout.razor
│ │ │ ├── NavMenu.razor
│ │ │ ├── RedirectToLogin.razor
│ │ │ ├── Spinner.razor
│ │ │ └── Toast.razor
│ │ ├── _Imports.razor
│ │ └── wwwroot/
│ │ ├── appsettings.Development.json
│ │ ├── appsettings.Docker.json
│ │ ├── appsettings.json
│ │ └── css/
│ │ ├── admin.css
│ │ └── open-iconic/
│ │ ├── FONT-LICENSE
│ │ ├── ICON-LICENSE
│ │ ├── README.md
│ │ └── font/
│ │ └── fonts/
│ │ └── open-iconic.otf
│ ├── BlazorShared/
│ │ ├── Attributes/
│ │ │ └── EndpointAttribute.cs
│ │ ├── Authorization/
│ │ │ ├── ClaimValue.cs
│ │ │ ├── Constants.cs
│ │ │ └── UserInfo.cs
│ │ ├── BaseUrlConfiguration.cs
│ │ ├── BlazorShared.csproj
│ │ ├── Interfaces/
│ │ │ ├── ICatalogItemService.cs
│ │ │ ├── ICatalogLookupDataService.cs
│ │ │ └── ILookupDataResponse.cs
│ │ └── Models/
│ │ ├── CatalogBrand.cs
│ │ ├── CatalogBrandResponse.cs
│ │ ├── CatalogItem.cs
│ │ ├── CatalogType.cs
│ │ ├── CatalogTypeResponse.cs
│ │ ├── CreateCatalogItemRequest.cs
│ │ ├── CreateCatalogItemResponse.cs
│ │ ├── DeleteCatalogItemResponse.cs
│ │ ├── EditCatalogItemResponse.cs
│ │ ├── ErrorDetails.cs
│ │ ├── LookupData.cs
│ │ └── PagedCatalogItemResponse.cs
│ ├── Infrastructure/
│ │ ├── Data/
│ │ │ ├── CatalogContext.cs
│ │ │ ├── CatalogContextSeed.cs
│ │ │ ├── Config/
│ │ │ │ ├── BasketConfiguration.cs
│ │ │ │ ├── BasketItemConfiguration.cs
│ │ │ │ ├── CatalogBrandConfiguration.cs
│ │ │ │ ├── CatalogItemConfiguration.cs
│ │ │ │ ├── CatalogTypeConfiguration.cs
│ │ │ │ ├── OrderConfiguration.cs
│ │ │ │ └── OrderItemConfiguration.cs
│ │ │ ├── EfRepository.cs
│ │ │ ├── FileItem.cs
│ │ │ ├── Migrations/
│ │ │ │ ├── 20201202111507_InitialModel.Designer.cs
│ │ │ │ ├── 20201202111507_InitialModel.cs
│ │ │ │ ├── 20211026175614_FixBuyerId.Designer.cs
│ │ │ │ ├── 20211026175614_FixBuyerId.cs
│ │ │ │ ├── 20211231093753_FixShipToAddress.Designer.cs
│ │ │ │ ├── 20211231093753_FixShipToAddress.cs
│ │ │ │ └── CatalogContextModelSnapshot.cs
│ │ │ └── Queries/
│ │ │ └── BasketQueryService.cs
│ │ ├── Dependencies.cs
│ │ ├── Identity/
│ │ │ ├── AppIdentityDbContext.cs
│ │ │ ├── AppIdentityDbContextSeed.cs
│ │ │ ├── ApplicationUser.cs
│ │ │ ├── IdentityTokenClaimService.cs
│ │ │ ├── Migrations/
│ │ │ │ ├── 20201202111612_InitialIdentityModel.Designer.cs
│ │ │ │ ├── 20201202111612_InitialIdentityModel.cs
│ │ │ │ └── AppIdentityDbContextModelSnapshot.cs
│ │ │ └── UserNotFoundException.cs
│ │ ├── Infrastructure.csproj
│ │ ├── Logging/
│ │ │ └── LoggerAdapter.cs
│ │ └── Services/
│ │ └── EmailSender.cs
│ ├── PublicApi/
│ │ ├── AuthEndpoints/
│ │ │ ├── AuthenticateEndpoint.AuthenticateRequest.cs
│ │ │ ├── AuthenticateEndpoint.AuthenticateResponse.cs
│ │ │ ├── AuthenticateEndpoint.ClaimValue.cs
│ │ │ ├── AuthenticateEndpoint.UserInfo.cs
│ │ │ └── AuthenticateEndpoint.cs
│ │ ├── BaseMessage.cs
│ │ ├── BaseRequest.cs
│ │ ├── BaseResponse.cs
│ │ ├── CatalogBrandEndpoints/
│ │ │ ├── CatalogBrandDto.cs
│ │ │ ├── CatalogBrandListEndpoint.ListCatalogBrandsResponse.cs
│ │ │ └── CatalogBrandListEndpoint.cs
│ │ ├── CatalogItemEndpoints/
│ │ │ ├── CatalogItemDto.cs
│ │ │ ├── CatalogItemGetByIdEndpoint.GetByIdCatalogItemRequest.cs
│ │ │ ├── CatalogItemGetByIdEndpoint.GetByIdCatalogItemResponse.cs
│ │ │ ├── CatalogItemGetByIdEndpoint.cs
│ │ │ ├── CatalogItemListPagedEndpoint.ListPagedCatalogItemRequest.cs
│ │ │ ├── CatalogItemListPagedEndpoint.ListPagedCatalogItemResponse.cs
│ │ │ ├── CatalogItemListPagedEndpoint.cs
│ │ │ ├── CreateCatalogItemEndpoint.CreateCatalogItemRequest.cs
│ │ │ ├── CreateCatalogItemEndpoint.CreateCatalogItemResponse.cs
│ │ │ ├── CreateCatalogItemEndpoint.cs
│ │ │ ├── DeleteCatalogItemEndpoint.DeleteCatalogItemRequest.cs
│ │ │ ├── DeleteCatalogItemEndpoint.DeleteCatalogItemResponse.cs
│ │ │ ├── DeleteCatalogItemEndpoint.cs
│ │ │ ├── UpdateCatalogItemEndpoint.UpdateCatalogItemRequest.cs
│ │ │ ├── UpdateCatalogItemEndpoint.UpdateCatalogItemResponse.cs
│ │ │ └── UpdateCatalogItemEndpoint.cs
│ │ ├── CatalogTypeEndpoints/
│ │ │ ├── CatalogTypeDto.cs
│ │ │ ├── CatalogTypeListEndpoint.ListCatalogTypesResponse.cs
│ │ │ └── CatalogTypeListEndpoint.cs
│ │ ├── CustomSchemaFilters.cs
│ │ ├── Dockerfile
│ │ ├── ImageValidators.cs
│ │ ├── MappingProfile.cs
│ │ ├── Middleware/
│ │ │ └── ExceptionMiddleware.cs
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── launchSettings.json
│ │ ├── PublicApi.csproj
│ │ ├── README.md
│ │ ├── appsettings.Development.json
│ │ ├── appsettings.Docker.json
│ │ └── appsettings.json
│ └── Web/
│ ├── .config/
│ │ └── dotnet-tools.json
│ ├── Areas/
│ │ └── Identity/
│ │ ├── IdentityHostingStartup.cs
│ │ └── Pages/
│ │ ├── Account/
│ │ │ ├── ConfirmEmail.cshtml
│ │ │ ├── ConfirmEmail.cshtml.cs
│ │ │ ├── Login.cshtml
│ │ │ ├── Login.cshtml.cs
│ │ │ ├── Logout.cshtml
│ │ │ ├── Logout.cshtml.cs
│ │ │ ├── Register.cshtml
│ │ │ ├── Register.cshtml.cs
│ │ │ └── _ViewImports.cshtml
│ │ ├── _ValidationScriptsPartial.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── Configuration/
│ │ ├── ConfigureCookieSettings.cs
│ │ ├── ConfigureCoreServices.cs
│ │ ├── ConfigureWebServices.cs
│ │ └── RevokeAuthenticationEvents.cs
│ ├── Constants.cs
│ ├── Controllers/
│ │ ├── Api/
│ │ │ └── BaseApiController.cs
│ │ ├── ManageController.cs
│ │ ├── OrderController.cs
│ │ └── UserController.cs
│ ├── Dockerfile
│ ├── Extensions/
│ │ ├── CacheHelpers.cs
│ │ ├── EmailSenderExtensions.cs
│ │ └── UrlHelperExtensions.cs
│ ├── Features/
│ │ ├── MyOrders/
│ │ │ ├── GetMyOrders.cs
│ │ │ └── GetMyOrdersHandler.cs
│ │ └── OrderDetails/
│ │ ├── GetOrderDetails.cs
│ │ └── GetOrderDetailsHandler.cs
│ ├── HealthChecks/
│ │ ├── ApiHealthCheck.cs
│ │ └── HomePageHealthCheck.cs
│ ├── Interfaces/
│ │ ├── IBasketViewModelService.cs
│ │ ├── ICatalogItemViewModelService.cs
│ │ └── ICatalogViewModelService.cs
│ ├── Pages/
│ │ ├── Admin/
│ │ │ ├── EditCatalogItem.cshtml
│ │ │ ├── EditCatalogItem.cshtml.cs
│ │ │ ├── Index.cshtml
│ │ │ └── Index.cshtml.cs
│ │ ├── Basket/
│ │ │ ├── BasketItemViewModel.cs
│ │ │ ├── BasketViewModel.cs
│ │ │ ├── Checkout.cshtml
│ │ │ ├── Checkout.cshtml.cs
│ │ │ ├── Index.cshtml
│ │ │ ├── Index.cshtml.cs
│ │ │ ├── Success.cshtml
│ │ │ └── Success.cshtml.cs
│ │ ├── Error.cshtml
│ │ ├── Error.cshtml.cs
│ │ ├── FeatureDiagnostics.cshtml
│ │ ├── Index.cshtml
│ │ ├── Index.cshtml.cs
│ │ ├── Privacy.cshtml
│ │ ├── Privacy.cshtml.cs
│ │ ├── SettingsViewModel.cs
│ │ ├── Shared/
│ │ │ ├── Components/
│ │ │ │ └── BasketComponent/
│ │ │ │ ├── Basket.cs
│ │ │ │ └── Default.cshtml
│ │ │ ├── _editCatalog.cshtml
│ │ │ ├── _pagination.cshtml
│ │ │ └── _product.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Services/
│ │ ├── BasketViewModelService.cs
│ │ ├── CachedCatalogViewModelService.cs
│ │ ├── CatalogItemViewModelService.cs
│ │ └── CatalogViewModelService.cs
│ ├── SlugifyParameterTransformer.cs
│ ├── ViewModels/
│ │ ├── Account/
│ │ │ ├── LoginViewModel.cs
│ │ │ ├── LoginWith2faViewModel.cs
│ │ │ ├── RegisterViewModel.cs
│ │ │ └── ResetPasswordViewModel.cs
│ │ ├── BasketComponentViewModel.cs
│ │ ├── CatalogIndexViewModel.cs
│ │ ├── CatalogItemViewModel.cs
│ │ ├── File/
│ │ │ └── FileViewModel.cs
│ │ ├── Manage/
│ │ │ ├── ChangePasswordViewModel.cs
│ │ │ ├── EnableAuthenticatorViewModel.cs
│ │ │ ├── ExternalLoginsViewModel.cs
│ │ │ ├── IndexViewModel.cs
│ │ │ ├── RemoveLoginViewModel.cs
│ │ │ ├── SetPasswordViewModel.cs
│ │ │ ├── ShowRecoveryCodesViewModel.cs
│ │ │ └── TwoFactorAuthenticationViewModel.cs
│ │ ├── OrderDetailViewModel.cs
│ │ ├── OrderItemViewModel.cs
│ │ ├── OrderViewModel.cs
│ │ └── PaginationInfoViewModel.cs
│ ├── Views/
│ │ ├── Account/
│ │ │ ├── Lockout.cshtml
│ │ │ └── LoginWith2fa.cshtml
│ │ ├── Manage/
│ │ │ ├── ChangePassword.cshtml
│ │ │ ├── Disable2fa.cshtml
│ │ │ ├── EnableAuthenticator.cshtml
│ │ │ ├── ExternalLogins.cshtml
│ │ │ ├── GenerateRecoveryCodes.cshtml
│ │ │ ├── ManageNavPages.cs
│ │ │ ├── MyAccount.cshtml
│ │ │ ├── ResetAuthenticator.cshtml
│ │ │ ├── SetPassword.cshtml
│ │ │ ├── ShowRecoverCodes.cshtml
│ │ │ ├── TwoFactorAuthentication.cshtml
│ │ │ ├── _Layout.cshtml
│ │ │ ├── _ManageNav.cshtml
│ │ │ ├── _StatusMessage.cshtml
│ │ │ └── _ViewImports.cshtml
│ │ ├── Order/
│ │ │ ├── Detail.cshtml
│ │ │ └── MyOrders.cshtml
│ │ ├── Shared/
│ │ │ ├── Components/
│ │ │ │ └── Basket/
│ │ │ │ └── Default.cshtml
│ │ │ ├── Error.cshtml
│ │ │ ├── _CookieConsentPartial.cshtml
│ │ │ ├── _Layout.cshtml
│ │ │ ├── _LoginPartial.cshtml
│ │ │ └── _ValidationScriptsPartial.cshtml
│ │ ├── _ViewImports.cshtml
│ │ └── _ViewStart.cshtml
│ ├── Web.csproj
│ ├── appsettings.Development.json
│ ├── appsettings.Docker.json
│ ├── appsettings.json
│ ├── bundleconfig.json
│ ├── compilerconfig.json
│ ├── compilerconfig.json.defaults
│ ├── key-768c1632-cf7b-41a9-bb7a-bff228ae8fba.xml
│ ├── libman.json
│ └── wwwroot/
│ ├── css/
│ │ ├── _variables.css
│ │ ├── _variables.scss
│ │ ├── app.component.css
│ │ ├── app.component.scss
│ │ ├── app.css
│ │ ├── basket/
│ │ │ ├── basket-status/
│ │ │ │ ├── basket-status.component.css
│ │ │ │ └── basket-status.component.scss
│ │ │ ├── basket.component.css
│ │ │ └── basket.component.scss
│ │ ├── catalog/
│ │ │ ├── catalog.component.css
│ │ │ ├── catalog.component.scss
│ │ │ └── pager.css
│ │ ├── orders/
│ │ │ ├── orders.component.css
│ │ │ └── orders.component.scss
│ │ └── shared/
│ │ └── components/
│ │ ├── header/
│ │ │ ├── header.css
│ │ │ └── header.scss
│ │ ├── identity/
│ │ │ ├── identity.css
│ │ │ └── identity.scss
│ │ └── pager/
│ │ ├── pager.css
│ │ └── pager.scss
│ └── js/
│ └── site.js
└── tests/
├── FunctionalTests/
│ ├── FunctionalTests.csproj
│ ├── PublicApi/
│ │ ├── ApiTestFixture.cs
│ │ ├── ApiTokenHelper.cs
│ │ └── AuthEndpoints/
│ │ └── AuthenticateEndpoint.cs
│ └── Web/
│ ├── Controllers/
│ │ ├── AccountControllerSignIn.cs
│ │ ├── CatalogControllerIndex.cs
│ │ └── OrderControllerIndex.cs
│ ├── Pages/
│ │ ├── Basket/
│ │ │ ├── BasketPageCheckout.cs
│ │ │ ├── CheckoutTest.cs
│ │ │ └── IndexTest.cs
│ │ └── HomePageOnGet.cs
│ ├── WebPageHelpers.cs
│ └── WebTestFixture.cs
├── IntegrationTests/
│ ├── IntegrationTests.csproj
│ └── Repositories/
│ ├── BasketRepositoryTests/
│ │ └── SetQuantities.cs
│ └── OrderRepositoryTests/
│ ├── GetById.cs
│ └── GetByIdWithItemsAsync.cs
├── PublicApiIntegrationTests/
│ ├── ApiTokenHelper.cs
│ ├── AuthEndpoints/
│ │ └── AuthenticateEndpointTest.cs
│ ├── CatalogItemEndpoints/
│ │ ├── CatalogItemGetByIdEndpointTest.cs
│ │ ├── CatalogItemListPagedEndpoint.cs
│ │ ├── CreateCatalogItemEndpointTest.cs
│ │ └── DeleteCatalogItemEndpointTest.cs
│ ├── ProgramTest.cs
│ ├── PublicApiIntegrationTests.csproj
│ └── appsettings.test.json
└── UnitTests/
├── ApplicationCore/
│ ├── Entities/
│ │ ├── BasketTests/
│ │ │ ├── BasketAddItem.cs
│ │ │ ├── BasketRemoveEmptyItems.cs
│ │ │ └── BasketTotalItems.cs
│ │ └── OrderTests/
│ │ └── OrderTotal.cs
│ ├── Extensions/
│ │ ├── JsonExtensions.cs
│ │ ├── TestChild.cs
│ │ └── TestParent.cs
│ ├── Services/
│ │ └── BasketServiceTests/
│ │ ├── AddItemToBasket.cs
│ │ ├── DeleteBasket.cs
│ │ └── TransferBasket.cs
│ └── Specifications/
│ ├── BasketWithItemsSpecification.cs
│ ├── CatalogFilterPaginatedSpecification.cs
│ ├── CatalogFilterSpecification.cs
│ ├── CatalogItemsSpecification.cs
│ └── CustomerOrdersWithItemsSpecification.cs
├── Builders/
│ ├── AddressBuilder.cs
│ ├── BasketBuilder.cs
│ └── OrderBuilder.cs
├── MediatorHandlers/
│ └── OrdersTests/
│ ├── GetMyOrders.cs
│ └── GetOrderDetails.cs
├── UnitTests.csproj
└── Web/
└── Extensions/
└── CacheHelpersTests/
├── GenerateBrandsCacheKey.cs
├── GenerateCatalogItemCacheKey.cs
└── GenerateTypesCacheKey.cs
SYMBOL INDEX (723 symbols across 252 files)
FILE: src/ApplicationCore/CatalogSettings.cs
class CatalogSettings (line 3) | public class CatalogSettings
FILE: src/ApplicationCore/Constants/AuthorizationConstants.cs
class AuthorizationConstants (line 3) | public class AuthorizationConstants
FILE: src/ApplicationCore/Entities/BaseEntity.cs
class BaseEntity (line 5) | public abstract class BaseEntity
FILE: src/ApplicationCore/Entities/BasketAggregate/Basket.cs
class Basket (line 8) | public class Basket : BaseEntity, IAggregateRoot
method Basket (line 17) | public Basket(string buyerId)
method AddItem (line 22) | public void AddItem(int catalogItemId, decimal unitPrice, int quantity...
method RemoveEmptyItems (line 33) | public void RemoveEmptyItems()
method SetNewBuyerId (line 38) | public void SetNewBuyerId(string buyerId)
FILE: src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs
class BasketItem (line 5) | public class BasketItem : BaseEntity
method BasketItem (line 13) | public BasketItem(int catalogItemId, int quantity, decimal unitPrice)
method AddQuantity (line 20) | public void AddQuantity(int quantity)
method SetQuantity (line 27) | public void SetQuantity(int quantity)
FILE: src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs
class Buyer (line 7) | public class Buyer : BaseEntity, IAggregateRoot
method Buyer (line 16) | private Buyer() { }
method Buyer (line 18) | public Buyer(string identity) : this()
FILE: src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs
class PaymentMethod (line 3) | public class PaymentMethod : BaseEntity
FILE: src/ApplicationCore/Entities/CatalogBrand.cs
class CatalogBrand (line 5) | public class CatalogBrand : BaseEntity, IAggregateRoot
method CatalogBrand (line 8) | public CatalogBrand(string brand)
FILE: src/ApplicationCore/Entities/CatalogItem.cs
class CatalogItem (line 7) | public class CatalogItem : BaseEntity, IAggregateRoot
method CatalogItem (line 18) | public CatalogItem(int catalogTypeId,
method UpdateDetails (line 33) | public void UpdateDetails(CatalogItemDetails details)
method UpdateBrand (line 44) | public void UpdateBrand(int catalogBrandId)
method UpdateType (line 50) | public void UpdateType(int catalogTypeId)
method UpdatePictureUri (line 56) | public void UpdatePictureUri(string pictureName)
type CatalogItemDetails (line 66) | public readonly record struct CatalogItemDetails
FILE: src/ApplicationCore/Entities/CatalogType.cs
class CatalogType (line 5) | public class CatalogType : BaseEntity, IAggregateRoot
method CatalogType (line 8) | public CatalogType(string type)
FILE: src/ApplicationCore/Entities/OrderAggregate/Address.cs
class Address (line 3) | public class Address // ValueObject
method Address (line 16) | private Address() { }
method Address (line 18) | public Address(string street, string city, string state, string countr...
FILE: src/ApplicationCore/Entities/OrderAggregate/CatalogItemOrdered.cs
class CatalogItemOrdered (line 9) | public class CatalogItemOrdered // ValueObject
method CatalogItemOrdered (line 11) | public CatalogItemOrdered(int catalogItemId, string productName, strin...
method CatalogItemOrdered (line 23) | private CatalogItemOrdered() {}
FILE: src/ApplicationCore/Entities/OrderAggregate/Order.cs
class Order (line 8) | public class Order : BaseEntity, IAggregateRoot
method Order (line 11) | private Order() {}
method Order (line 13) | public Order(string buyerId, Address shipToAddress, List<OrderItem> it...
method Total (line 38) | public decimal Total()
FILE: src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs
class OrderItem (line 3) | public class OrderItem : BaseEntity
method OrderItem (line 10) | private OrderItem() {}
method OrderItem (line 12) | public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, in...
FILE: src/ApplicationCore/Exceptions/BasketNotFoundException.cs
class BasketNotFoundException (line 5) | public class BasketNotFoundException : Exception
method BasketNotFoundException (line 7) | public BasketNotFoundException(int basketId) : base($"No basket found ...
FILE: src/ApplicationCore/Exceptions/DuplicateException.cs
class DuplicateException (line 5) | public class DuplicateException : Exception
method DuplicateException (line 7) | public DuplicateException(string message) : base(message)
FILE: src/ApplicationCore/Exceptions/EmptyBasketOnCheckoutException.cs
class EmptyBasketOnCheckoutException (line 5) | public class EmptyBasketOnCheckoutException : Exception
method EmptyBasketOnCheckoutException (line 7) | public EmptyBasketOnCheckoutException()
method EmptyBasketOnCheckoutException (line 12) | public EmptyBasketOnCheckoutException(string message) : base(message)
method EmptyBasketOnCheckoutException (line 16) | public EmptyBasketOnCheckoutException(string message, Exception innerE...
FILE: src/ApplicationCore/Extensions/GuardExtensions.cs
class BasketGuards (line 8) | public static class BasketGuards
method EmptyBasketOnCheckout (line 10) | public static void EmptyBasketOnCheckout(this IGuardClause guardClause...
FILE: src/ApplicationCore/Extensions/JsonExtensions.cs
class JsonExtensions (line 5) | public static class JsonExtensions
method FromJson (line 12) | public static T? FromJson<T>(this string json) =>
method ToJson (line 15) | public static string ToJson<T>(this T obj) =>
FILE: src/ApplicationCore/Interfaces/IAggregateRoot.cs
type IAggregateRoot (line 3) | public interface IAggregateRoot
FILE: src/ApplicationCore/Interfaces/IAppLogger.cs
type IAppLogger (line 7) | public interface IAppLogger<T>
method LogInformation (line 9) | void LogInformation(string message, params object[] args);
method LogWarning (line 10) | void LogWarning(string message, params object[] args);
FILE: src/ApplicationCore/Interfaces/IBasketQueryService.cs
type IBasketQueryService (line 8) | public interface IBasketQueryService
method CountTotalBasketItems (line 10) | Task<int> CountTotalBasketItems(string username);
FILE: src/ApplicationCore/Interfaces/IBasketService.cs
type IBasketService (line 8) | public interface IBasketService
method TransferBasketAsync (line 10) | Task TransferBasketAsync(string anonymousId, string userName);
method AddItemToBasket (line 11) | Task<Basket> AddItemToBasket(string username, int catalogItemId, decim...
method SetQuantities (line 12) | Task<Result<Basket>> SetQuantities(int basketId, Dictionary<string, in...
method DeleteBasketAsync (line 13) | Task DeleteBasketAsync(int basketId);
FILE: src/ApplicationCore/Interfaces/IEmailSender.cs
type IEmailSender (line 5) | public interface IEmailSender
method SendEmailAsync (line 7) | Task SendEmailAsync(string email, string subject, string message);
FILE: src/ApplicationCore/Interfaces/IOrderService.cs
type IOrderService (line 6) | public interface IOrderService
method CreateOrderAsync (line 8) | Task CreateOrderAsync(int basketId, Address shippingAddress);
FILE: src/ApplicationCore/Interfaces/IReadRepository.cs
type IReadRepository (line 5) | public interface IReadRepository<T> : IReadRepositoryBase<T> where T : c...
FILE: src/ApplicationCore/Interfaces/IRepository.cs
type IRepository (line 5) | public interface IRepository<T> : IRepositoryBase<T> where T : class, IA...
FILE: src/ApplicationCore/Interfaces/ITokenClaimsService.cs
type ITokenClaimsService (line 5) | public interface ITokenClaimsService
method GetTokenAsync (line 7) | Task<string> GetTokenAsync(string userName);
FILE: src/ApplicationCore/Interfaces/IUriComposer.cs
type IUriComposer (line 3) | public interface IUriComposer
method ComposePicUri (line 5) | string ComposePicUri(string uriTemplate);
FILE: src/ApplicationCore/Services/BasketService.cs
class BasketService (line 11) | public class BasketService : IBasketService
method BasketService (line 16) | public BasketService(IRepository<Basket> basketRepository,
method AddItemToBasket (line 23) | public async Task<Basket> AddItemToBasket(string username, int catalog...
method DeleteBasketAsync (line 40) | public async Task DeleteBasketAsync(int basketId)
method SetQuantities (line 47) | public async Task<Result<Basket>> SetQuantities(int basketId, Dictiona...
method TransferBasketAsync (line 66) | public async Task TransferBasketAsync(string anonymousId, string userN...
FILE: src/ApplicationCore/Services/OrderService.cs
class OrderService (line 12) | public class OrderService : IOrderService
method OrderService (line 19) | public OrderService(IRepository<Basket> basketRepository,
method CreateOrderAsync (line 30) | public async Task CreateOrderAsync(int basketId, Address shippingAddress)
FILE: src/ApplicationCore/Services/UriComposer.cs
class UriComposer (line 5) | public class UriComposer : IUriComposer
method UriComposer (line 9) | public UriComposer(CatalogSettings catalogSettings) => _catalogSetting...
method ComposePicUri (line 11) | public string ComposePicUri(string uriTemplate)
FILE: src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs
class BasketWithItemsSpecification (line 6) | public sealed class BasketWithItemsSpecification : Specification<Basket>
method BasketWithItemsSpecification (line 8) | public BasketWithItemsSpecification(int basketId)
method BasketWithItemsSpecification (line 15) | public BasketWithItemsSpecification(string buyerId)
FILE: src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs
class CatalogFilterPaginatedSpecification (line 6) | public class CatalogFilterPaginatedSpecification : Specification<Catalog...
method CatalogFilterPaginatedSpecification (line 8) | public CatalogFilterPaginatedSpecification(int skip, int take, int? br...
FILE: src/ApplicationCore/Specifications/CatalogFilterSpecification.cs
class CatalogFilterSpecification (line 6) | public class CatalogFilterSpecification : Specification<CatalogItem>
method CatalogFilterSpecification (line 8) | public CatalogFilterSpecification(int? brandId, int? typeId)
FILE: src/ApplicationCore/Specifications/CatalogItemNameSpecification.cs
class CatalogItemNameSpecification (line 6) | public class CatalogItemNameSpecification : Specification<CatalogItem>
method CatalogItemNameSpecification (line 8) | public CatalogItemNameSpecification(string catalogItemName)
FILE: src/ApplicationCore/Specifications/CatalogItemsSpecification.cs
class CatalogItemsSpecification (line 8) | public class CatalogItemsSpecification : Specification<CatalogItem>
method CatalogItemsSpecification (line 10) | public CatalogItemsSpecification(params int[] ids)
FILE: src/ApplicationCore/Specifications/CustomerOrdersSpecification.cs
class CustomerOrdersSpecification (line 6) | public class CustomerOrdersSpecification : Specification<Order>
method CustomerOrdersSpecification (line 8) | public CustomerOrdersSpecification(string buyerId)
FILE: src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs
class CustomerOrdersWithItemsSpecification (line 6) | public class CustomerOrdersWithItemsSpecification : Specification<Order>
method CustomerOrdersWithItemsSpecification (line 8) | public CustomerOrdersWithItemsSpecification(string buyerId)
FILE: src/ApplicationCore/Specifications/OrderWithItemsByIdSpec.cs
class OrderWithItemsByIdSpec (line 6) | public class OrderWithItemsByIdSpec : Specification<Order>
method OrderWithItemsByIdSpec (line 8) | public OrderWithItemsByIdSpec(int orderId)
FILE: src/BlazorAdmin/CustomAuthStateProvider.cs
class CustomAuthStateProvider (line 13) | public class CustomAuthStateProvider : AuthenticationStateProvider
method CustomAuthStateProvider (line 24) | public CustomAuthStateProvider(HttpClient httpClient,
method GetAuthenticationStateAsync (line 31) | public override async Task<AuthenticationState> GetAuthenticationState...
method GetUser (line 36) | private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = false)
method FetchUser (line 50) | private async Task<ClaimsPrincipal> FetchUser()
FILE: src/BlazorAdmin/Helpers/BlazorComponent.cs
class BlazorComponent (line 5) | public class BlazorComponent : ComponentBase
method OnInitialized (line 9) | protected override void OnInitialized()
method CallRequestRefresh (line 15) | public void CallRequestRefresh()
method DoRefresh (line 20) | private void DoRefresh()
FILE: src/BlazorAdmin/Helpers/BlazorLayoutComponent.cs
class BlazorLayoutComponent (line 5) | public class BlazorLayoutComponent : LayoutComponentBase
method OnInitialized (line 9) | protected override void OnInitialized()
method CallRequestRefresh (line 15) | public void CallRequestRefresh()
method DoRefresh (line 20) | private void DoRefresh()
FILE: src/BlazorAdmin/Helpers/RefreshBroadcast.cs
class RefreshBroadcast (line 5) | internal sealed class RefreshBroadcast
method RefreshBroadcast (line 14) | private RefreshBroadcast()
method CallRequestRefresh (line 19) | public void CallRequestRefresh()
FILE: src/BlazorAdmin/Helpers/ToastComponent.cs
class ToastComponent (line 7) | public class ToastComponent : ComponentBase, IDisposable
method OnInitialized (line 40) | protected override void OnInitialized()
method ShowToast (line 45) | private void ShowToast(string message, ToastLevel level)
method HideToast (line 51) | private void HideToast()
method BuildToastSettings (line 56) | private void BuildToastSettings(ToastLevel level, string message)
method Dispose (line 83) | public void Dispose()
FILE: src/BlazorAdmin/JavaScript/Cookies.cs
class Cookies (line 6) | public class Cookies
method Cookies (line 10) | public Cookies(IJSRuntime jsRuntime)
method DeleteCookie (line 15) | public async Task DeleteCookie(string name)
method GetCookie (line 20) | public async Task<string> GetCookie(string name)
FILE: src/BlazorAdmin/JavaScript/Css.cs
class Css (line 6) | public class Css
method Css (line 10) | public Css(IJSRuntime jsRuntime)
method ShowBodyOverflow (line 15) | public async Task ShowBodyOverflow()
method HideBodyOverflow (line 20) | public async Task<string> HideBodyOverflow()
FILE: src/BlazorAdmin/JavaScript/JSInteropConstants.cs
class JSInteropConstants (line 3) | public static class JSInteropConstants
FILE: src/BlazorAdmin/JavaScript/Route.cs
class Route (line 6) | public class Route
method Route (line 10) | public Route(IJSRuntime jsRuntime)
method RouteOutside (line 15) | public async Task RouteOutside(string path)
FILE: src/BlazorAdmin/Pages/CatalogItemPage/List.razor.cs
class List (line 9) | public partial class List : BlazorComponent
method OnAfterRenderAsync (line 29) | protected override async Task OnAfterRenderAsync(bool firstRender)
method DetailsClick (line 43) | private async void DetailsClick(int id)
method CreateClick (line 48) | private async Task CreateClick()
method EditClick (line 53) | private async Task EditClick(int id)
method DeleteClick (line 58) | private async Task DeleteClick(int id)
method ReloadCatalogItems (line 63) | private async Task ReloadCatalogItems()
FILE: src/BlazorAdmin/Services/CacheEntry.cs
class CacheEntry (line 5) | public class CacheEntry<T>
method CacheEntry (line 7) | public CacheEntry(T item)
method CacheEntry (line 11) | public CacheEntry()
FILE: src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs
class CachedCatalogItemServiceDecorator (line 12) | public class CachedCatalogItemServiceDecorator : ICatalogItemService
method CachedCatalogItemServiceDecorator (line 18) | public CachedCatalogItemServiceDecorator(ILocalStorageService localSto...
method ListPaged (line 27) | public async Task<List<CatalogItem>> ListPaged(int pageSize)
method List (line 51) | public async Task<List<CatalogItem>> List()
method GetById (line 75) | public async Task<CatalogItem> GetById(int id)
method Create (line 80) | public async Task<CatalogItem> Create(CreateCatalogItemRequest catalog...
method Edit (line 88) | public async Task<CatalogItem> Edit(CatalogItem catalogItem)
method Delete (line 96) | public async Task<string> Delete(int id)
method RefreshLocalStorageList (line 104) | private async Task RefreshLocalStorageList()
FILE: src/BlazorAdmin/Services/CachedCatalogLookupDataServiceDecorator .cs
class CachedCatalogLookupDataServiceDecorator (line 11) | public class CachedCatalogLookupDataServiceDecorator<TLookupData, TReponse>
method CachedCatalogLookupDataServiceDecorator (line 20) | public CachedCatalogLookupDataServiceDecorator(ILocalStorageService lo...
method List (line 29) | public async Task<List<TLookupData>> List()
FILE: src/BlazorAdmin/Services/CatalogItemService.cs
class CatalogItemService (line 11) | public class CatalogItemService : ICatalogItemService
method CatalogItemService (line 18) | public CatalogItemService(ICatalogLookupDataService<CatalogBrand> bran...
method Create (line 29) | public async Task<CatalogItem> Create(CreateCatalogItemRequest catalog...
method Edit (line 35) | public async Task<CatalogItem> Edit(CatalogItem catalogItem)
method Delete (line 40) | public async Task<string> Delete(int catalogItemId)
method GetById (line 45) | public async Task<CatalogItem> GetById(int id)
method ListPaged (line 59) | public async Task<List<CatalogItem>> ListPaged(int pageSize)
method List (line 78) | public async Task<List<CatalogItem>> List()
FILE: src/BlazorAdmin/Services/CatalogLookupDataService.cs
class CatalogLookupDataService (line 16) | public class CatalogLookupDataService<TLookupData, TReponse>
method CatalogLookupDataService (line 26) | public CatalogLookupDataService(HttpClient httpClient,
method List (line 35) | public async Task<List<TLookupData>> List()
FILE: src/BlazorAdmin/Services/HttpService.cs
class HttpService (line 11) | public class HttpService
method HttpService (line 18) | public HttpService(HttpClient httpClient, IOptions<BaseUrlConfiguratio...
method HttpGet (line 25) | public async Task<T> HttpGet<T>(string uri)
method HttpDelete (line 37) | public async Task<T> HttpDelete<T>(string uri, int id)
method HttpPost (line 49) | public async Task<T> HttpPost<T>(string uri, object dataToSend)
method HttpPut (line 69) | public async Task<T> HttpPut<T>(string uri, object dataToSend)
method ToJson (line 84) | private StringContent ToJson(object obj)
method FromHttpResponseMessage (line 89) | private async Task<T> FromHttpResponseMessage<T>(HttpResponseMessage r...
FILE: src/BlazorAdmin/Services/ToastService.cs
type ToastLevel (line 6) | public enum ToastLevel
class ToastService (line 14) | public class ToastService : IDisposable
method ShowToast (line 19) | public void ShowToast(string message, ToastLevel level)
method StartCountdown (line 24) | private void StartCountdown()
method SetCountdown (line 37) | private void SetCountdown()
method HideToast (line 46) | private void HideToast(object source, ElapsedEventArgs args)
method Dispose (line 50) | public void Dispose()
FILE: src/BlazorAdmin/ServicesConfiguration.cs
class ServicesConfiguration (line 8) | public static class ServicesConfiguration
method AddBlazorServices (line 10) | public static IServiceCollection AddBlazorServices(this IServiceCollec...
FILE: src/BlazorAdmin/Shared/CustomInputSelect.cs
class CustomInputSelect (line 10) | public class CustomInputSelect<TValue> : InputSelect<TValue>
method TryParseValueFromString (line 12) | protected override bool TryParseValueFromString(string value, out TVal...
FILE: src/BlazorShared/Attributes/EndpointAttribute.cs
class EndpointAttribute (line 5) | public class EndpointAttribute : Attribute
FILE: src/BlazorShared/Authorization/ClaimValue.cs
class ClaimValue (line 3) | public class ClaimValue
method ClaimValue (line 5) | public ClaimValue()
method ClaimValue (line 9) | public ClaimValue(string type, string value)
FILE: src/BlazorShared/Authorization/Constants.cs
class Constants (line 3) | public static class Constants
class Roles (line 5) | public static class Roles
FILE: src/BlazorShared/Authorization/UserInfo.cs
class UserInfo (line 5) | public class UserInfo
FILE: src/BlazorShared/BaseUrlConfiguration.cs
class BaseUrlConfiguration (line 3) | public class BaseUrlConfiguration
FILE: src/BlazorShared/Interfaces/ICatalogItemService.cs
type ICatalogItemService (line 7) | public interface ICatalogItemService
method Create (line 9) | Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem);
method Edit (line 10) | Task<CatalogItem> Edit(CatalogItem catalogItem);
method Delete (line 11) | Task<string> Delete(int id);
method GetById (line 12) | Task<CatalogItem> GetById(int id);
method ListPaged (line 13) | Task<List<CatalogItem>> ListPaged(int pageSize);
method List (line 14) | Task<List<CatalogItem>> List();
FILE: src/BlazorShared/Interfaces/ICatalogLookupDataService.cs
type ICatalogLookupDataService (line 7) | public interface ICatalogLookupDataService<TLookupData> where TLookupDat...
method List (line 9) | Task<List<TLookupData>> List();
FILE: src/BlazorShared/Interfaces/ILookupDataResponse.cs
type ILookupDataResponse (line 6) | public interface ILookupDataResponse<TLookupData> where TLookupData : Lo...
FILE: src/BlazorShared/Models/CatalogBrand.cs
class CatalogBrand (line 5) | [Endpoint(Name = "catalog-brands")]
FILE: src/BlazorShared/Models/CatalogBrandResponse.cs
class CatalogBrandResponse (line 7) | public class CatalogBrandResponse : ILookupDataResponse<CatalogBrand>
FILE: src/BlazorShared/Models/CatalogItem.cs
class CatalogItem (line 9) | public class CatalogItem
method IsValidImage (line 37) | public static string IsValidImage(string pictureName, string pictureBa...
method DataToBase64 (line 63) | public static async Task<string> DataToBase64(IFileListEntry fileItem)
method IsExtensionValid (line 78) | private static bool IsExtensionValid(string fileName)
FILE: src/BlazorShared/Models/CatalogType.cs
class CatalogType (line 5) | [Endpoint(Name = "catalog-types")]
FILE: src/BlazorShared/Models/CatalogTypeResponse.cs
class CatalogTypeResponse (line 7) | public class CatalogTypeResponse : ILookupDataResponse<CatalogType>
FILE: src/BlazorShared/Models/CreateCatalogItemRequest.cs
class CreateCatalogItemRequest (line 5) | public class CreateCatalogItemRequest
FILE: src/BlazorShared/Models/CreateCatalogItemResponse.cs
class CreateCatalogItemResponse (line 3) | public class CreateCatalogItemResponse
FILE: src/BlazorShared/Models/DeleteCatalogItemResponse.cs
class DeleteCatalogItemResponse (line 3) | public class DeleteCatalogItemResponse
FILE: src/BlazorShared/Models/EditCatalogItemResponse.cs
class EditCatalogItemResult (line 3) | public class EditCatalogItemResult
FILE: src/BlazorShared/Models/ErrorDetails.cs
class ErrorDetails (line 5) | public class ErrorDetails
method ToString (line 9) | public override string ToString()
FILE: src/BlazorShared/Models/LookupData.cs
class LookupData (line 3) | public abstract class LookupData
FILE: src/BlazorShared/Models/PagedCatalogItemResponse.cs
class PagedCatalogItemResponse (line 5) | public class PagedCatalogItemResponse
FILE: src/Infrastructure/Data/CatalogContext.cs
class CatalogContext (line 9) | public class CatalogContext : DbContext
method CatalogContext (line 12) | public CatalogContext(DbContextOptions<CatalogContext> options) : base...
method OnModelCreating (line 22) | protected override void OnModelCreating(ModelBuilder builder)
FILE: src/Infrastructure/Data/CatalogContextSeed.cs
class CatalogContextSeed (line 10) | public class CatalogContextSeed
method SeedAsync (line 12) | public static async Task SeedAsync(CatalogContext catalogContext,
method GetPreconfiguredCatalogBrands (line 60) | static IEnumerable<CatalogBrand> GetPreconfiguredCatalogBrands()
method GetPreconfiguredCatalogTypes (line 72) | static IEnumerable<CatalogType> GetPreconfiguredCatalogTypes()
method GetPreconfiguredItems (line 83) | static IEnumerable<CatalogItem> GetPreconfiguredItems()
FILE: src/Infrastructure/Data/Config/BasketConfiguration.cs
class BasketConfiguration (line 7) | public class BasketConfiguration : IEntityTypeConfiguration<Basket>
method Configure (line 9) | public void Configure(EntityTypeBuilder<Basket> builder)
FILE: src/Infrastructure/Data/Config/BasketItemConfiguration.cs
class BasketItemConfiguration (line 7) | public class BasketItemConfiguration : IEntityTypeConfiguration<BasketItem>
method Configure (line 9) | public void Configure(EntityTypeBuilder<BasketItem> builder)
FILE: src/Infrastructure/Data/Config/CatalogBrandConfiguration.cs
class CatalogBrandConfiguration (line 7) | public class CatalogBrandConfiguration : IEntityTypeConfiguration<Catalo...
method Configure (line 9) | public void Configure(EntityTypeBuilder<CatalogBrand> builder)
FILE: src/Infrastructure/Data/Config/CatalogItemConfiguration.cs
class CatalogItemConfiguration (line 7) | public class CatalogItemConfiguration : IEntityTypeConfiguration<Catalog...
method Configure (line 9) | public void Configure(EntityTypeBuilder<CatalogItem> builder)
FILE: src/Infrastructure/Data/Config/CatalogTypeConfiguration.cs
class CatalogTypeConfiguration (line 7) | public class CatalogTypeConfiguration : IEntityTypeConfiguration<Catalog...
method Configure (line 9) | public void Configure(EntityTypeBuilder<CatalogType> builder)
FILE: src/Infrastructure/Data/Config/OrderConfiguration.cs
class OrderConfiguration (line 7) | public class OrderConfiguration : IEntityTypeConfiguration<Order>
method Configure (line 9) | public void Configure(EntityTypeBuilder<Order> builder)
FILE: src/Infrastructure/Data/Config/OrderItemConfiguration.cs
class OrderItemConfiguration (line 7) | public class OrderItemConfiguration : IEntityTypeConfiguration<OrderItem>
method Configure (line 9) | public void Configure(EntityTypeBuilder<OrderItem> builder)
FILE: src/Infrastructure/Data/EfRepository.cs
class EfRepository (line 6) | public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IR...
method EfRepository (line 8) | public EfRepository(CatalogContext dbContext) : base(dbContext)
FILE: src/Infrastructure/Data/FileItem.cs
class FileItem (line 3) | public class FileItem
FILE: src/Infrastructure/Data/Migrations/20201202111507_InitialModel.Designer.cs
class InitialModel (line 12) | [DbContext(typeof(CatalogContext))]
method BuildTargetModel (line 16) | protected override void BuildTargetModel(ModelBuilder modelBuilder)
FILE: src/Infrastructure/Data/Migrations/20201202111507_InitialModel.cs
class InitialModel (line 6) | public partial class InitialModel : Migration
method Up (line 8) | protected override void Up(MigrationBuilder migrationBuilder)
method Down (line 174) | protected override void Down(MigrationBuilder migrationBuilder)
FILE: src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.Designer.cs
class FixBuyerId (line 12) | [DbContext(typeof(CatalogContext))]
method BuildTargetModel (line 16) | protected override void BuildTargetModel(ModelBuilder modelBuilder)
FILE: src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.cs
class FixBuyerId (line 5) | public partial class FixBuyerId : Migration
method Up (line 7) | protected override void Up(MigrationBuilder migrationBuilder)
method Down (line 31) | protected override void Down(MigrationBuilder migrationBuilder)
FILE: src/Infrastructure/Data/Migrations/20211231093753_FixShipToAddress.Designer.cs
class FixShipToAddress (line 14) | [DbContext(typeof(CatalogContext))]
method BuildTargetModel (line 18) | protected override void BuildTargetModel(ModelBuilder modelBuilder)
FILE: src/Infrastructure/Data/Migrations/20211231093753_FixShipToAddress.cs
class FixShipToAddress (line 7) | public partial class FixShipToAddress : Migration
method Up (line 9) | protected override void Up(MigrationBuilder migrationBuilder)
method Down (line 60) | protected override void Down(MigrationBuilder migrationBuilder)
FILE: src/Infrastructure/Data/Migrations/CatalogContextModelSnapshot.cs
class CatalogContextModelSnapshot (line 13) | [DbContext(typeof(CatalogContext))]
method BuildModel (line 16) | protected override void BuildModel(ModelBuilder modelBuilder)
FILE: src/Infrastructure/Data/Queries/BasketQueryService.cs
class BasketQueryService (line 8) | public class BasketQueryService : IBasketQueryService
method BasketQueryService (line 12) | public BasketQueryService(CatalogContext dbContext)
method CountTotalBasketItems (line 22) | public async Task<int> CountTotalBasketItems(string username)
FILE: src/Infrastructure/Dependencies.cs
class Dependencies (line 9) | public static class Dependencies
method ConfigureServices (line 11) | public static void ConfigureServices(IConfiguration configuration, ISe...
FILE: src/Infrastructure/Identity/AppIdentityDbContext.cs
class AppIdentityDbContext (line 7) | public class AppIdentityDbContext : IdentityDbContext<ApplicationUser>
method AppIdentityDbContext (line 9) | public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> opt...
method OnModelCreating (line 14) | protected override void OnModelCreating(ModelBuilder builder)
FILE: src/Infrastructure/Identity/AppIdentityDbContextSeed.cs
class AppIdentityDbContextSeed (line 8) | public class AppIdentityDbContextSeed
method SeedAsync (line 10) | public static async Task SeedAsync(AppIdentityDbContext identityDbCont...
FILE: src/Infrastructure/Identity/ApplicationUser.cs
class ApplicationUser (line 5) | public class ApplicationUser : IdentityUser
FILE: src/Infrastructure/Identity/IdentityTokenClaimService.cs
class IdentityTokenClaimService (line 14) | public class IdentityTokenClaimService : ITokenClaimsService
method IdentityTokenClaimService (line 18) | public IdentityTokenClaimService(UserManager<ApplicationUser> userMana...
method GetTokenAsync (line 23) | public async Task<string> GetTokenAsync(string userName)
FILE: src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.Designer.cs
class InitialIdentityModel (line 12) | [DbContext(typeof(AppIdentityDbContext))]
method BuildTargetModel (line 16) | protected override void BuildTargetModel(ModelBuilder modelBuilder)
FILE: src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.cs
class InitialIdentityModel (line 6) | public partial class InitialIdentityModel : Migration
method Up (line 8) | protected override void Up(MigrationBuilder migrationBuilder)
method Down (line 195) | protected override void Down(MigrationBuilder migrationBuilder)
FILE: src/Infrastructure/Identity/Migrations/AppIdentityDbContextModelSnapshot.cs
class AppIdentityDbContextModelSnapshot (line 11) | [DbContext(typeof(AppIdentityDbContext))]
method BuildModel (line 14) | protected override void BuildModel(ModelBuilder modelBuilder)
FILE: src/Infrastructure/Identity/UserNotFoundException.cs
class UserNotFoundException (line 5) | public class UserNotFoundException : Exception
method UserNotFoundException (line 7) | public UserNotFoundException(string userName) : base($"No user found w...
FILE: src/Infrastructure/Logging/LoggerAdapter.cs
class LoggerAdapter (line 6) | public class LoggerAdapter<T> : IAppLogger<T>
method LoggerAdapter (line 9) | public LoggerAdapter(ILoggerFactory loggerFactory)
method LogWarning (line 14) | public void LogWarning(string message, params object[] args)
method LogInformation (line 19) | public void LogInformation(string message, params object[] args)
FILE: src/Infrastructure/Services/EmailSender.cs
class EmailSender (line 8) | public class EmailSender : IEmailSender
method SendEmailAsync (line 10) | public Task SendEmailAsync(string email, string subject, string message)
FILE: src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateRequest.cs
class AuthenticateRequest (line 3) | public class AuthenticateRequest : BaseRequest
FILE: src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateResponse.cs
class AuthenticateResponse (line 5) | public class AuthenticateResponse : BaseResponse
method AuthenticateResponse (line 7) | public AuthenticateResponse(Guid correlationId) : base(correlationId)
method AuthenticateResponse (line 11) | public AuthenticateResponse()
FILE: src/PublicApi/AuthEndpoints/AuthenticateEndpoint.ClaimValue.cs
class ClaimValue (line 3) | public class ClaimValue
method ClaimValue (line 5) | public ClaimValue()
method ClaimValue (line 9) | public ClaimValue(string type, string value)
FILE: src/PublicApi/AuthEndpoints/AuthenticateEndpoint.UserInfo.cs
class UserInfo (line 5) | public class UserInfo
FILE: src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs
class AuthenticateEndpoint (line 15) | public class AuthenticateEndpoint : EndpointBaseAsync
method AuthenticateEndpoint (line 22) | public AuthenticateEndpoint(SignInManager<ApplicationUser> signInManager,
method HandleAsync (line 29) | [HttpPost("api/authenticate")]
FILE: src/PublicApi/BaseMessage.cs
class BaseMessage (line 8) | public abstract class BaseMessage
method CorrelationId (line 14) | public Guid CorrelationId() => _correlationId;
FILE: src/PublicApi/BaseRequest.cs
class BaseRequest (line 6) | public abstract class BaseRequest : BaseMessage
FILE: src/PublicApi/BaseResponse.cs
class BaseResponse (line 8) | public abstract class BaseResponse : BaseMessage
method BaseResponse (line 10) | public BaseResponse(Guid correlationId) : base()
method BaseResponse (line 15) | public BaseResponse()
FILE: src/PublicApi/CatalogBrandEndpoints/CatalogBrandDto.cs
class CatalogBrandDto (line 3) | public class CatalogBrandDto
FILE: src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.ListCatalogBrandsResponse.cs
class ListCatalogBrandsResponse (line 6) | public class ListCatalogBrandsResponse : BaseResponse
method ListCatalogBrandsResponse (line 8) | public ListCatalogBrandsResponse(Guid correlationId) : base(correlatio...
method ListCatalogBrandsResponse (line 12) | public ListCatalogBrandsResponse()
FILE: src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs
class CatalogBrandListEndpoint (line 16) | public class CatalogBrandListEndpoint : IEndpoint<IResult, IRepository<C...
method CatalogBrandListEndpoint (line 20) | public CatalogBrandListEndpoint(IMapper mapper)
method AddRoute (line 25) | public void AddRoute(IEndpointRouteBuilder app)
method HandleAsync (line 36) | public async Task<IResult> HandleAsync(IRepository<CatalogBrand> catal...
FILE: src/PublicApi/CatalogItemEndpoints/CatalogItemDto.cs
class CatalogItemDto (line 3) | public class CatalogItemDto
FILE: src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemRequest.cs
class GetByIdCatalogItemRequest (line 3) | public class GetByIdCatalogItemRequest : BaseRequest
method GetByIdCatalogItemRequest (line 7) | public GetByIdCatalogItemRequest(int catalogItemId)
FILE: src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemResponse.cs
class GetByIdCatalogItemResponse (line 5) | public class GetByIdCatalogItemResponse : BaseResponse
method GetByIdCatalogItemResponse (line 7) | public GetByIdCatalogItemResponse(Guid correlationId) : base(correlati...
method GetByIdCatalogItemResponse (line 11) | public GetByIdCatalogItemResponse()
FILE: src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs
class CatalogItemGetByIdEndpoint (line 14) | public class CatalogItemGetByIdEndpoint : IEndpoint<IResult, GetByIdCata...
method CatalogItemGetByIdEndpoint (line 18) | public CatalogItemGetByIdEndpoint(IUriComposer uriComposer)
method AddRoute (line 23) | public void AddRoute(IEndpointRouteBuilder app)
method HandleAsync (line 34) | public async Task<IResult> HandleAsync(GetByIdCatalogItemRequest reque...
FILE: src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemRequest.cs
class ListPagedCatalogItemRequest (line 3) | public class ListPagedCatalogItemRequest : BaseRequest
method ListPagedCatalogItemRequest (line 10) | public ListPagedCatalogItemRequest(int? pageSize, int? pageIndex, int?...
FILE: src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemResponse.cs
class ListPagedCatalogItemResponse (line 6) | public class ListPagedCatalogItemResponse : BaseResponse
method ListPagedCatalogItemResponse (line 8) | public ListPagedCatalogItemResponse(Guid correlationId) : base(correla...
method ListPagedCatalogItemResponse (line 12) | public ListPagedCatalogItemResponse()
FILE: src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs
class CatalogItemListPagedEndpoint (line 18) | public class CatalogItemListPagedEndpoint : IEndpoint<IResult, ListPaged...
method CatalogItemListPagedEndpoint (line 23) | public CatalogItemListPagedEndpoint(IUriComposer uriComposer, IMapper ...
method AddRoute (line 29) | public void AddRoute(IEndpointRouteBuilder app)
method HandleAsync (line 40) | public async Task<IResult> HandleAsync(ListPagedCatalogItemRequest req...
FILE: src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.CreateCatalogItemRequest.cs
class CreateCatalogItemRequest (line 3) | public class CreateCatalogItemRequest : BaseRequest
FILE: src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.CreateCatalogItemResponse.cs
class CreateCatalogItemResponse (line 5) | public class CreateCatalogItemResponse : BaseResponse
method CreateCatalogItemResponse (line 7) | public CreateCatalogItemResponse(Guid correlationId) : base(correlatio...
method CreateCatalogItemResponse (line 11) | public CreateCatalogItemResponse()
FILE: src/PublicApi/CatalogItemEndpoints/CreateCatalogItemEndpoint.cs
class CreateCatalogItemEndpoint (line 18) | public class CreateCatalogItemEndpoint : IEndpoint<IResult, CreateCatalo...
method CreateCatalogItemEndpoint (line 22) | public CreateCatalogItemEndpoint(IUriComposer uriComposer)
method AddRoute (line 27) | public void AddRoute(IEndpointRouteBuilder app)
method HandleAsync (line 39) | public async Task<IResult> HandleAsync(CreateCatalogItemRequest reques...
FILE: src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.DeleteCatalogItemRequest.cs
class DeleteCatalogItemRequest (line 3) | public class DeleteCatalogItemRequest : BaseRequest
method DeleteCatalogItemRequest (line 7) | public DeleteCatalogItemRequest(int catalogItemId)
FILE: src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.DeleteCatalogItemResponse.cs
class DeleteCatalogItemResponse (line 5) | public class DeleteCatalogItemResponse : BaseResponse
method DeleteCatalogItemResponse (line 7) | public DeleteCatalogItemResponse(Guid correlationId) : base(correlatio...
method DeleteCatalogItemResponse (line 11) | public DeleteCatalogItemResponse()
FILE: src/PublicApi/CatalogItemEndpoints/DeleteCatalogItemEndpoint.cs
class DeleteCatalogItemEndpoint (line 16) | public class DeleteCatalogItemEndpoint : IEndpoint<IResult, DeleteCatalo...
method AddRoute (line 18) | public void AddRoute(IEndpointRouteBuilder app)
method HandleAsync (line 30) | public async Task<IResult> HandleAsync(DeleteCatalogItemRequest reques...
FILE: src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.UpdateCatalogItemRequest.cs
class UpdateCatalogItemRequest (line 5) | public class UpdateCatalogItemRequest : BaseRequest
FILE: src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.UpdateCatalogItemResponse.cs
class UpdateCatalogItemResponse (line 5) | public class UpdateCatalogItemResponse : BaseResponse
method UpdateCatalogItemResponse (line 7) | public UpdateCatalogItemResponse(Guid correlationId) : base(correlatio...
method UpdateCatalogItemResponse (line 11) | public UpdateCatalogItemResponse()
FILE: src/PublicApi/CatalogItemEndpoints/UpdateCatalogItemEndpoint.cs
class UpdateCatalogItemEndpoint (line 16) | public class UpdateCatalogItemEndpoint : IEndpoint<IResult, UpdateCatalo...
method UpdateCatalogItemEndpoint (line 20) | public UpdateCatalogItemEndpoint(IUriComposer uriComposer)
method AddRoute (line 25) | public void AddRoute(IEndpointRouteBuilder app)
method HandleAsync (line 37) | public async Task<IResult> HandleAsync(UpdateCatalogItemRequest reques...
FILE: src/PublicApi/CatalogTypeEndpoints/CatalogTypeDto.cs
class CatalogTypeDto (line 3) | public class CatalogTypeDto
FILE: src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.ListCatalogTypesResponse.cs
class ListCatalogTypesResponse (line 6) | public class ListCatalogTypesResponse : BaseResponse
method ListCatalogTypesResponse (line 8) | public ListCatalogTypesResponse(Guid correlationId) : base(correlationId)
method ListCatalogTypesResponse (line 12) | public ListCatalogTypesResponse()
FILE: src/PublicApi/CatalogTypeEndpoints/CatalogTypeListEndpoint.cs
class CatalogTypeListEndpoint (line 16) | public class CatalogTypeListEndpoint : IEndpoint<IResult, IRepository<Ca...
method CatalogTypeListEndpoint (line 20) | public CatalogTypeListEndpoint(IMapper mapper)
method AddRoute (line 25) | public void AddRoute(IEndpointRouteBuilder app)
method HandleAsync (line 36) | public async Task<IResult> HandleAsync(IRepository<CatalogType> catalo...
FILE: src/PublicApi/CustomSchemaFilters.cs
class CustomSchemaFilters (line 6) | public class CustomSchemaFilters : ISchemaFilter
method Apply (line 8) | public void Apply(OpenApiSchema schema, SchemaFilterContext context)
FILE: src/PublicApi/ImageValidators.cs
class ImageValidators (line 6) | public static class ImageValidators
method IsValidImage (line 10) | public static bool IsValidImage(this byte[] postedFile, string fileName)
method IsExtensionValid (line 15) | private static bool IsExtensionValid(string fileName)
FILE: src/PublicApi/MappingProfile.cs
class MappingProfile (line 9) | public class MappingProfile : Profile
method MappingProfile (line 11) | public MappingProfile()
FILE: src/PublicApi/Middleware/ExceptionMiddleware.cs
class ExceptionMiddleware (line 10) | public class ExceptionMiddleware
method ExceptionMiddleware (line 14) | public ExceptionMiddleware(RequestDelegate next)
method InvokeAsync (line 19) | public async Task InvokeAsync(HttpContext httpContext)
method HandleExceptionAsync (line 31) | private async Task HandleExceptionAsync(HttpContext context, Exception...
FILE: src/PublicApi/Program.cs
class Program (line 181) | public partial class Program { }
FILE: src/Web/Areas/Identity/IdentityHostingStartup.cs
class IdentityHostingStartup (line 6) | public class IdentityHostingStartup : IHostingStartup
method Configure (line 8) | public void Configure(IWebHostBuilder builder)
FILE: src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs
class ConfirmEmailModel (line 13) | [AllowAnonymous]
method ConfirmEmailModel (line 18) | public ConfirmEmailModel(UserManager<ApplicationUser> userManager)
method OnGetAsync (line 23) | public async Task<IActionResult> OnGetAsync(string userId, string code)
FILE: src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs
class LoginModel (line 13) | [AllowAnonymous]
method LoginModel (line 20) | public LoginModel(SignInManager<ApplicationUser> signInManager, ILogge...
class InputModel (line 37) | public class InputModel
method OnGetAsync (line 51) | public async Task OnGetAsync(string? returnUrl = null)
method OnPostAsync (line 68) | public async Task<IActionResult> OnPostAsync(string? returnUrl = null)
method TransferAnonymousBasketToUserAsync (line 106) | private async Task TransferAnonymousBasketToUserAsync(string? userName)
FILE: src/Web/Areas/Identity/Pages/Account/Logout.cshtml.cs
class LogoutModel (line 14) | public class LogoutModel : PageModel
method LogoutModel (line 20) | public LogoutModel(SignInManager<ApplicationUser> signInManager, ILogg...
method OnGet (line 27) | public void OnGet()
method OnPost (line 31) | public async Task<IActionResult> OnPost(string? returnUrl = null)
FILE: src/Web/Areas/Identity/Pages/Account/Register.cshtml.cs
class RegisterModel (line 17) | [AllowAnonymous]
method RegisterModel (line 25) | public RegisterModel(
class InputModel (line 42) | public class InputModel
method OnGet (line 61) | public void OnGet(string? returnUrl = null)
method OnPostAsync (line 66) | public async Task<IActionResult> OnPostAsync(string? returnUrl = null)
FILE: src/Web/Configuration/ConfigureCookieSettings.cs
class ConfigureCookieSettings (line 8) | public static class ConfigureCookieSettings
method AddCookieSettings (line 13) | public static IServiceCollection AddCookieSettings(this IServiceCollec...
FILE: src/Web/Configuration/ConfigureCoreServices.cs
class ConfigureCoreServices (line 10) | public static class ConfigureCoreServices
method AddCoreServices (line 12) | public static IServiceCollection AddCoreServices(this IServiceCollecti...
FILE: src/Web/Configuration/ConfigureWebServices.cs
class ConfigureWebServices (line 7) | public static class ConfigureWebServices
method AddWebServices (line 9) | public static IServiceCollection AddWebServices(this IServiceCollectio...
FILE: src/Web/Configuration/RevokeAuthenticationEvents.cs
class RevokeAuthenticationEvents (line 12) | public class RevokeAuthenticationEvents : CookieAuthenticationEvents
method RevokeAuthenticationEvents (line 17) | public RevokeAuthenticationEvents(IMemoryCache cache, ILogger<RevokeAu...
method ValidatePrincipal (line 23) | public override async Task ValidatePrincipal(CookieValidatePrincipalCo...
FILE: src/Web/Constants.cs
class Constants (line 3) | public static class Constants
FILE: src/Web/Controllers/Api/BaseApiController.cs
class BaseApiController (line 6) | [Route("api/[controller]/[action]")]
FILE: src/Web/Controllers/ManageController.cs
class ManageController (line 15) | [ApiExplorerSettings(IgnoreApi = true)]
method ManageController (line 29) | public ManageController(
method MyAccount (line 46) | [HttpGet]
method MyAccount (line 67) | [HttpPost]
method SendVerificationEmail (line 106) | [HttpPost]
method ChangePassword (line 136) | [HttpGet]
method ChangePassword (line 155) | [HttpPost]
method SetPassword (line 185) | [HttpGet]
method SetPassword (line 205) | [HttpPost]
method ExternalLogins (line 233) | [HttpGet]
method LinkLogin (line 252) | [HttpPost]
method LinkLoginCallback (line 265) | [HttpGet]
method RemoveLogin (line 293) | [HttpPost]
method TwoFactorAuthentication (line 318) | [HttpGet]
method Disable2faWarning (line 337) | [HttpGet]
method Disable2fa (line 354) | [HttpPost]
method EnableAuthenticator (line 374) | [HttpGet]
method ShowRecoveryCodes (line 389) | [HttpGet]
method EnableAuthenticator (line 403) | [HttpPost]
method ResetAuthenticatorWarning (line 440) | [HttpGet]
method ResetAuthenticator (line 446) | [HttpPost]
method GenerateRecoveryCodes (line 463) | [HttpPost]
method GenerateRecoveryCodesWarning (line 486) | [HttpGet]
method AddErrors (line 503) | private void AddErrors(IdentityResult result)
method FormatKey (line 511) | private string FormatKey(string unformattedKey)
method GenerateQrCodeUri (line 528) | private string GenerateQrCodeUri(string email, string unformattedKey)
method LoadSharedKeyAndQrCodeUriAsync (line 537) | private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user...
FILE: src/Web/Controllers/OrderController.cs
class OrderController (line 10) | [ApiExplorerSettings(IgnoreApi = true)]
method OrderController (line 17) | public OrderController(IMediator mediator)
method MyOrders (line 22) | [HttpGet]
method Detail (line 31) | [HttpGet("{orderId}")]
FILE: src/Web/Controllers/UserController.cs
class UserController (line 15) | [Route("[controller]")]
method UserController (line 24) | public UserController(ITokenClaimsService tokenClaimsService,
method GetCurrentUser (line 35) | [HttpGet]
method Logout (line 41) | [Route("Logout")]
method CreateUserInfo (line 60) | private async Task<UserInfo> CreateUserInfo(ClaimsPrincipal claimsPrin...
FILE: src/Web/Extensions/CacheHelpers.cs
class CacheHelpers (line 5) | public static class CacheHelpers
method GenerateCatalogItemCacheKey (line 10) | public static string GenerateCatalogItemCacheKey(int pageIndex, int it...
method GenerateBrandsCacheKey (line 15) | public static string GenerateBrandsCacheKey()
method GenerateTypesCacheKey (line 20) | public static string GenerateTypesCacheKey()
FILE: src/Web/Extensions/EmailSenderExtensions.cs
class EmailSenderExtensions (line 7) | public static class EmailSenderExtensions
method SendEmailConfirmationAsync (line 9) | public static Task SendEmailConfirmationAsync(this IEmailSender emailS...
FILE: src/Web/Extensions/UrlHelperExtensions.cs
class UrlHelperExtensions (line 3) | public static class UrlHelperExtensions
method EmailConfirmationLink (line 5) | public static string? EmailConfirmationLink(this IUrlHelper urlHelper,...
FILE: src/Web/Features/MyOrders/GetMyOrders.cs
class GetMyOrders (line 6) | public class GetMyOrders : IRequest<IEnumerable<OrderViewModel>>
method GetMyOrders (line 10) | public GetMyOrders(string userName)
FILE: src/Web/Features/MyOrders/GetMyOrdersHandler.cs
class GetMyOrdersHandler (line 9) | public class GetMyOrdersHandler : IRequestHandler<GetMyOrders, IEnumerab...
method GetMyOrdersHandler (line 13) | public GetMyOrdersHandler(IReadRepository<Order> orderRepository)
method Handle (line 18) | public async Task<IEnumerable<OrderViewModel>> Handle(GetMyOrders requ...
FILE: src/Web/Features/OrderDetails/GetOrderDetails.cs
class GetOrderDetails (line 6) | public class GetOrderDetails : IRequest<OrderDetailViewModel>
method GetOrderDetails (line 11) | public GetOrderDetails(string userName, int orderId)
FILE: src/Web/Features/OrderDetails/GetOrderDetailsHandler.cs
class GetOrderDetailsHandler (line 9) | public class GetOrderDetailsHandler : IRequestHandler<GetOrderDetails, O...
method GetOrderDetailsHandler (line 13) | public GetOrderDetailsHandler(IReadRepository<Order> orderRepository)
method Handle (line 18) | public async Task<OrderDetailViewModel?> Handle(GetOrderDetails request,
FILE: src/Web/HealthChecks/ApiHealthCheck.cs
class ApiHealthCheck (line 10) | public class ApiHealthCheck : IHealthCheck
method ApiHealthCheck (line 14) | public ApiHealthCheck(IOptions<BaseUrlConfiguration> baseUrlConfigurat...
method CheckHealthAsync (line 19) | public async Task<HealthCheckResult> CheckHealthAsync(
FILE: src/Web/HealthChecks/HomePageHealthCheck.cs
class HomePageHealthCheck (line 9) | public class HomePageHealthCheck : IHealthCheck
method HomePageHealthCheck (line 13) | public HomePageHealthCheck(IHttpContextAccessor httpContextAccessor)
method CheckHealthAsync (line 18) | public async Task<HealthCheckResult> CheckHealthAsync(
FILE: src/Web/Interfaces/IBasketViewModelService.cs
type IBasketViewModelService (line 6) | public interface IBasketViewModelService
method GetOrCreateBasketForUser (line 8) | Task<BasketViewModel> GetOrCreateBasketForUser(string userName);
method CountTotalBasketItems (line 9) | Task<int> CountTotalBasketItems(string username);
method Map (line 10) | Task<BasketViewModel> Map(Basket basket);
FILE: src/Web/Interfaces/ICatalogItemViewModelService.cs
type ICatalogItemViewModelService (line 6) | public interface ICatalogItemViewModelService
method UpdateCatalogItem (line 8) | Task UpdateCatalogItem(CatalogItemViewModel viewModel);
FILE: src/Web/Interfaces/ICatalogViewModelService.cs
type ICatalogViewModelService (line 8) | public interface ICatalogViewModelService
method GetCatalogItems (line 10) | Task<CatalogIndexViewModel> GetCatalogItems(int pageIndex, int itemsPa...
method GetBrands (line 11) | Task<IEnumerable<SelectListItem>> GetBrands();
method GetTypes (line 12) | Task<IEnumerable<SelectListItem>> GetTypes();
FILE: src/Web/Pages/Admin/EditCatalogItem.cshtml.cs
class EditCatalogItemModel (line 11) | [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRA...
method EditCatalogItemModel (line 16) | public EditCatalogItemModel(ICatalogItemViewModelService catalogItemVi...
method OnGet (line 24) | public void OnGet(CatalogItemViewModel catalogModel)
method OnPostAsync (line 29) | public async Task<IActionResult> OnPostAsync()
FILE: src/Web/Pages/Admin/Index.cshtml.cs
class IndexModel (line 12) | [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRA...
method IndexModel (line 15) | public IndexModel()
FILE: src/Web/Pages/Basket/BasketItemViewModel.cs
class BasketItemViewModel (line 5) | public class BasketItemViewModel
FILE: src/Web/Pages/Basket/BasketViewModel.cs
class BasketViewModel (line 3) | public class BasketViewModel
method Total (line 9) | public decimal Total()
FILE: src/Web/Pages/Basket/Checkout.cshtml.cs
class CheckoutModel (line 14) | [Authorize]
method CheckoutModel (line 24) | public CheckoutModel(IBasketService basketService,
method OnGet (line 39) | public async Task OnGet()
method OnPost (line 44) | public async Task<IActionResult> OnPost(IEnumerable<BasketItemViewMode...
method SetBasketModelAsync (line 70) | private async Task SetBasketModelAsync()
method GetOrSetBasketCookieAndUserName (line 84) | private void GetOrSetBasketCookieAndUserName()
FILE: src/Web/Pages/Basket/Index.cshtml.cs
class IndexModel (line 11) | public class IndexModel : PageModel
method IndexModel (line 17) | public IndexModel(IBasketService basketService,
method OnGet (line 28) | public async Task OnGet()
method OnPost (line 33) | public async Task<IActionResult> OnPost(CatalogItemViewModel productDe...
method OnPostUpdate (line 55) | public async Task OnPostUpdate(IEnumerable<BasketItemViewModel> items)
method GetOrSetBasketCookieAndUserName (line 68) | private string GetOrSetBasketCookieAndUserName()
FILE: src/Web/Pages/Basket/Success.cshtml.cs
class SuccessModel (line 11) | [Authorize]
method OnGet (line 14) | public void OnGet()
FILE: src/Web/Pages/Error.cshtml.cs
class ErrorModel (line 7) | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoSt...
method OnGet (line 14) | public void OnGet()
FILE: src/Web/Pages/Index.cshtml.cs
class IndexModel (line 8) | public class IndexModel : PageModel
method IndexModel (line 13) | public IndexModel(ICatalogViewModelService catalogViewModelService, IO...
method OnGet (line 21) | public async Task OnGet(CatalogIndexViewModel catalogModel, int? pageId)
FILE: src/Web/Pages/Privacy.cshtml.cs
class PrivacyModel (line 5) | public class PrivacyModel : PageModel
method OnGet (line 7) | public void OnGet()
FILE: src/Web/Pages/SettingsViewModel.cs
class SettingsViewModel (line 3) | public class SettingsViewModel
FILE: src/Web/Pages/Shared/Components/BasketComponent/Basket.cs
class Basket (line 12) | public class Basket : ViewComponent
method Basket (line 17) | public Basket(IBasketViewModelService basketService,
method InvokeAsync (line 24) | public async Task<IViewComponentResult> InvokeAsync()
method CountTotalBasketItems (line 33) | private async Task<int> CountTotalBasketItems()
method GetAnnonymousIdFromCookie (line 48) | private string? GetAnnonymousIdFromCookie()
FILE: src/Web/Services/BasketViewModelService.cs
class BasketViewModelService (line 10) | public class BasketViewModelService : IBasketViewModelService
method BasketViewModelService (line 17) | public BasketViewModelService(IRepository<Basket> basketRepository,
method GetOrCreateBasketForUser (line 28) | public async Task<BasketViewModel> GetOrCreateBasketForUser(string use...
method CreateBasketForUser (line 41) | private async Task<BasketViewModel> CreateBasketForUser(string userId)
method GetBasketItems (line 53) | private async Task<List<BasketItemViewModel>> GetBasketItems(IReadOnly...
method Map (line 77) | public async Task<BasketViewModel> Map(Basket basket)
method CountTotalBasketItems (line 87) | public async Task<int> CountTotalBasketItems(string username)
FILE: src/Web/Services/CachedCatalogViewModelService.cs
class CachedCatalogViewModelService (line 10) | public class CachedCatalogViewModelService : ICatalogViewModelService
method CachedCatalogViewModelService (line 15) | public CachedCatalogViewModelService(IMemoryCache cache,
method GetBrands (line 22) | public async Task<IEnumerable<SelectListItem>> GetBrands()
method GetCatalogItems (line 31) | public async Task<CatalogIndexViewModel> GetCatalogItems(int pageIndex...
method GetTypes (line 42) | public async Task<IEnumerable<SelectListItem>> GetTypes()
FILE: src/Web/Services/CatalogItemViewModelService.cs
class CatalogItemViewModelService (line 9) | public class CatalogItemViewModelService : ICatalogItemViewModelService
method CatalogItemViewModelService (line 13) | public CatalogItemViewModelService(IRepository<CatalogItem> catalogIte...
method UpdateCatalogItem (line 18) | public async Task UpdateCatalogItem(CatalogItemViewModel viewModel)
FILE: src/Web/Services/CatalogViewModelService.cs
class CatalogViewModelService (line 18) | public class CatalogViewModelService : ICatalogViewModelService
method CatalogViewModelService (line 26) | public CatalogViewModelService(
method GetCatalogItems (line 40) | public async Task<CatalogIndexViewModel> GetCatalogItems(int pageIndex...
method GetBrands (line 80) | public async Task<IEnumerable<SelectListItem>> GetBrands()
method GetTypes (line 96) | public async Task<IEnumerable<SelectListItem>> GetTypes()
FILE: src/Web/SlugifyParameterTransformer.cs
class SlugifyParameterTransformer (line 6) | public class SlugifyParameterTransformer : IOutboundParameterTransformer
method TransformOutbound (line 8) | public string? TransformOutbound(object? value)
FILE: src/Web/ViewModels/Account/LoginViewModel.cs
class LoginViewModel (line 5) | public class LoginViewModel
FILE: src/Web/ViewModels/Account/LoginWith2faViewModel.cs
class LoginWith2faViewModel (line 5) | public class LoginWith2faViewModel
FILE: src/Web/ViewModels/Account/RegisterViewModel.cs
class RegisterViewModel (line 5) | public class RegisterViewModel
FILE: src/Web/ViewModels/Account/ResetPasswordViewModel.cs
class ResetPasswordViewModel (line 5) | public class ResetPasswordViewModel
FILE: src/Web/ViewModels/BasketComponentViewModel.cs
class BasketComponentViewModel (line 3) | public class BasketComponentViewModel
FILE: src/Web/ViewModels/CatalogIndexViewModel.cs
class CatalogIndexViewModel (line 5) | public class CatalogIndexViewModel
FILE: src/Web/ViewModels/CatalogItemViewModel.cs
class CatalogItemViewModel (line 3) | public class CatalogItemViewModel
FILE: src/Web/ViewModels/File/FileViewModel.cs
class FileViewModel (line 3) | public class FileViewModel
FILE: src/Web/ViewModels/Manage/ChangePasswordViewModel.cs
class ChangePasswordViewModel (line 5) | public class ChangePasswordViewModel
FILE: src/Web/ViewModels/Manage/EnableAuthenticatorViewModel.cs
class EnableAuthenticatorViewModel (line 7) | public class EnableAuthenticatorViewModel
FILE: src/Web/ViewModels/Manage/ExternalLoginsViewModel.cs
class ExternalLoginsViewModel (line 7) | public class ExternalLoginsViewModel
FILE: src/Web/ViewModels/Manage/IndexViewModel.cs
class IndexViewModel (line 5) | public class IndexViewModel
FILE: src/Web/ViewModels/Manage/RemoveLoginViewModel.cs
class RemoveLoginViewModel (line 5) | public class RemoveLoginViewModel
FILE: src/Web/ViewModels/Manage/SetPasswordViewModel.cs
class SetPasswordViewModel (line 5) | public class SetPasswordViewModel
FILE: src/Web/ViewModels/Manage/ShowRecoveryCodesViewModel.cs
class ShowRecoveryCodesViewModel (line 3) | public class ShowRecoveryCodesViewModel
FILE: src/Web/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs
class TwoFactorAuthenticationViewModel (line 3) | public class TwoFactorAuthenticationViewModel
FILE: src/Web/ViewModels/OrderDetailViewModel.cs
class OrderDetailViewModel (line 3) | public class OrderDetailViewModel : OrderViewModel
FILE: src/Web/ViewModels/OrderItemViewModel.cs
class OrderItemViewModel (line 3) | public class OrderItemViewModel
FILE: src/Web/ViewModels/OrderViewModel.cs
class OrderViewModel (line 5) | public class OrderViewModel
FILE: src/Web/ViewModels/PaginationInfoViewModel.cs
class PaginationInfoViewModel (line 3) | public class PaginationInfoViewModel
FILE: src/Web/Views/Manage/ManageNavPages.cs
class ManageNavPages (line 7) | public static class ManageNavPages
method IndexNavClass (line 19) | public static string IndexNavClass(ViewContext viewContext) => PageNav...
method ChangePasswordNavClass (line 21) | public static string ChangePasswordNavClass(ViewContext viewContext) =...
method ExternalLoginsNavClass (line 23) | public static string ExternalLoginsNavClass(ViewContext viewContext) =...
method TwoFactorAuthenticationNavClass (line 25) | public static string TwoFactorAuthenticationNavClass(ViewContext viewC...
method PageNavClass (line 27) | public static string PageNavClass(ViewContext viewContext, string page)
method AddActivePage (line 33) | public static void AddActivePage(this ViewDataDictionary viewData, str...
FILE: tests/FunctionalTests/PublicApi/ApiTestFixture.cs
class TestApiApplication (line 12) | public class TestApiApplication : WebApplicationFactory<AuthenticateEndp...
method CreateHost (line 16) | protected override IHost CreateHost(IHostBuilder builder)
FILE: tests/FunctionalTests/PublicApi/ApiTokenHelper.cs
class ApiTokenHelper (line 11) | public class ApiTokenHelper
method GetAdminUserToken (line 13) | public static string GetAdminUserToken()
method GetNormalUserToken (line 21) | public static string GetNormalUserToken()
method CreateToken (line 29) | private static string CreateToken(string userName, string[] roles)
FILE: tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs
class AccountControllerSignIn (line 8) | [Collection("Sequential")]
method AccountControllerSignIn (line 11) | public AccountControllerSignIn(TestApplication factory)
method ReturnsSignInScreenOnGet (line 21) | [Fact]
method RegexMatchesValidRequestVerificationToken (line 31) | [Fact]
method ReturnsFormWithRequestVerificationToken (line 45) | [Fact]
method ReturnsSuccessfulSignInOnPostWithValidCredentials (line 56) | [Fact]
method UpdatePhoneNumberProfile (line 76) | [Fact]
FILE: tests/FunctionalTests/Web/Controllers/CatalogControllerIndex.cs
class CatalogControllerIndex (line 7) | [Collection("Sequential")]
method CatalogControllerIndex (line 10) | public CatalogControllerIndex(TestApplication factory)
method ReturnsHomePageWithProductListing (line 17) | [Fact]
FILE: tests/FunctionalTests/Web/Controllers/OrderControllerIndex.cs
class OrderIndexOnGet (line 9) | [Collection("Sequential")]
method OrderIndexOnGet (line 12) | public OrderIndexOnGet(TestApplication factory)
method ReturnsRedirectGivenAnonymousUser (line 22) | [Fact]
FILE: tests/FunctionalTests/Web/Pages/Basket/BasketPageCheckout.cs
class BasketPageCheckout (line 6) | [Collection("Sequential")]
method BasketPageCheckout (line 9) | public BasketPageCheckout(TestApplication factory)
method RedirectsToLoginIfNotAuthenticated (line 19) | [Fact]
FILE: tests/FunctionalTests/Web/Pages/Basket/CheckoutTest.cs
class CheckoutTest (line 6) | [Collection("Sequential")]
method CheckoutTest (line 9) | public CheckoutTest(TestApplication factory)
method SucessfullyPay (line 19) | [Fact]
FILE: tests/FunctionalTests/Web/Pages/Basket/IndexTest.cs
class IndexTest (line 6) | [Collection("Sequential")]
method IndexTest (line 9) | public IndexTest(TestApplication factory)
method OnPostUpdateTo50Successfully (line 20) | [Fact]
method OnPostUpdateTo0EmptyBasket (line 60) | [Fact]
FILE: tests/FunctionalTests/Web/Pages/HomePageOnGet.cs
class HomePageOnGet (line 8) | [Collection("Sequential")]
method HomePageOnGet (line 11) | public HomePageOnGet(TestApplication factory)
method ReturnsHomePageWithProductListing (line 18) | [Fact]
FILE: tests/FunctionalTests/Web/WebPageHelpers.cs
class WebPageHelpers (line 5) | public static class WebPageHelpers
method GetRequestVerificationToken (line 9) | public static string GetRequestVerificationToken(string input)
method GetId (line 15) | public static string GetId(string input)
method RegexSearch (line 21) | private static string RegexSearch(string regexpression, string input)
FILE: tests/FunctionalTests/Web/WebTestFixture.cs
class TestApplication (line 15) | public class TestApplication : WebApplicationFactory<IBasketViewModelSer...
method CreateHost (line 19) | protected override IHost CreateHost(IHostBuilder builder)
FILE: tests/IntegrationTests/Repositories/BasketRepositoryTests/SetQuantities.cs
class SetQuantities (line 13) | public class SetQuantities
method SetQuantities (line 19) | public SetQuantities()
method RemoveEmptyQuantities (line 28) | [Fact]
FILE: tests/IntegrationTests/Repositories/OrderRepositoryTests/GetById.cs
class GetById (line 12) | public class GetById
method GetById (line 18) | public GetById(ITestOutputHelper output)
method GetsExistingOrder (line 28) | [Fact]
FILE: tests/IntegrationTests/Repositories/OrderRepositoryTests/GetByIdWithItemsAsync.cs
class GetByIdWithItemsAsync (line 13) | public class GetByIdWithItemsAsync
method GetByIdWithItemsAsync (line 19) | public GetByIdWithItemsAsync()
method GetOrderAndItemsByOrderIdWhenMultipleOrdersPresent (line 28) | [Fact]
FILE: tests/PublicApiIntegrationTests/ApiTokenHelper.cs
class ApiTokenHelper (line 11) | public class ApiTokenHelper
method GetAdminUserToken (line 13) | public static string GetAdminUserToken()
method GetNormalUserToken (line 21) | public static string GetNormalUserToken()
method CreateToken (line 29) | private static string CreateToken(string userName, string[] roles)
FILE: tests/PublicApiIntegrationTests/AuthEndpoints/AuthenticateEndpointTest.cs
class AuthenticateEndpoint (line 12) | [TestClass]
method ReturnsExpectedResultGivenCredentials (line 15) | [TestMethod]
FILE: tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemGetByIdEndpointTest.cs
class CatalogItemGetByIdEndpointTest (line 9) | [TestClass]
method ReturnsItemGivenValidId (line 12) | [TestMethod]
method ReturnsNotFoundGivenInvalidId (line 24) | [TestMethod]
FILE: tests/PublicApiIntegrationTests/CatalogItemEndpoints/CatalogItemListPagedEndpoint.cs
class CatalogItemListPagedEndpoint (line 13) | [TestClass]
method ReturnsFirst10CatalogItems (line 16) | [TestMethod]
method ReturnsCorrectCatalogItemsGivenPageIndex1 (line 28) | [TestMethod]
method SuccessFullMutipleParallelCall (line 52) | [DataTestMethod]
FILE: tests/PublicApiIntegrationTests/CatalogItemEndpoints/CreateCatalogItemEndpointTest.cs
class CreateCatalogItemEndpointTest (line 13) | [TestClass]
method ReturnsNotAuthorizedGivenNormalUserToken (line 23) | [TestMethod]
method ReturnsSuccessGivenValidNewItemAndAdminUserToken (line 35) | [TestMethod]
method GetValidNewItemJson (line 54) | private StringContent GetValidNewItemJson()
FILE: tests/PublicApiIntegrationTests/CatalogItemEndpoints/DeleteCatalogItemEndpointTest.cs
class DeleteCatalogItemEndpointTest (line 10) | [TestClass]
method ReturnsSuccessGivenValidIdAndAdminUserToken (line 13) | [TestMethod]
method ReturnsNotFoundGivenInvalidIdAndAdminUserToken (line 27) | [TestMethod]
FILE: tests/PublicApiIntegrationTests/ProgramTest.cs
class ProgramTest (line 7) | [TestClass]
method AssemblyInitialize (line 20) | [AssemblyInitialize]
FILE: tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketAddItem.cs
class BasketAddItem (line 8) | public class BasketAddItem
method AddsBasketItemIfNotPresent (line 15) | [Fact]
method IncrementsQuantityOfItemIfPresent (line 27) | [Fact]
method KeepsOriginalUnitPriceIfMoreItemsAdded (line 38) | [Fact]
method DefaultsToQuantityOfOne (line 49) | [Fact]
method CantAddItemWithNegativeQuantity (line 59) | [Fact]
method CantModifyQuantityToNegativeNumber (line 67) | [Fact]
FILE: tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketRemoveEmptyItems.cs
class BasketRemoveEmptyItems (line 6) | public class BasketRemoveEmptyItems
method RemovesEmptyBasketItems (line 12) | [Fact]
FILE: tests/UnitTests/ApplicationCore/Entities/BasketTests/BasketTotalItems.cs
class BasketTotalItems (line 6) | public class BasketTotalItems
method ReturnsTotalQuantityWithOneItem (line 13) | [Fact]
method ReturnsTotalQuantityWithMultipleItems (line 24) | [Fact]
FILE: tests/UnitTests/ApplicationCore/Entities/OrderTests/OrderTotal.cs
class OrderTotal (line 8) | public class OrderTotal
method IsZeroForNewOrder (line 12) | [Fact]
method IsCorrectGiven1Item (line 20) | [Fact]
method IsCorrectGiven3Items (line 32) | [Fact]
FILE: tests/UnitTests/ApplicationCore/Extensions/JsonExtensions.cs
class JsonExtensions (line 5) | public class JsonExtensions
method CorrectlySerializesAndDeserializesObject (line 7) | [Fact]
method CorrectlyDeserializesJson (line 27) | [
FILE: tests/UnitTests/ApplicationCore/Extensions/TestChild.cs
class TestChild (line 7) | [DebuggerDisplay("Id={Id}, Date={Date}")]
method Equals (line 14) | public bool Equals([AllowNull] TestChild other) =>
FILE: tests/UnitTests/ApplicationCore/Extensions/TestParent.cs
class TestParent (line 5) | public class TestParent : IEquatable<TestParent>
method Equals (line 13) | public bool Equals([AllowNull] TestParent other)
FILE: tests/UnitTests/ApplicationCore/Services/BasketServiceTests/AddItemToBasket.cs
class AddItemToBasket (line 12) | public class AddItemToBasket
method InvokesBasketRepositoryGetBySpecAsyncOnce (line 18) | [Fact]
method InvokesBasketRepositoryUpdateAsyncOnce (line 33) | [Fact]
FILE: tests/UnitTests/ApplicationCore/Services/BasketServiceTests/DeleteBasket.cs
class DeleteBasket (line 11) | public class DeleteBasket
method ShouldInvokeBasketRepositoryDeleteAsyncOnce (line 17) | [Fact]
FILE: tests/UnitTests/ApplicationCore/Services/BasketServiceTests/TransferBasket.cs
class TransferBasket (line 12) | public class TransferBasket
class Results (line 21) | public class Results<T>
method Results (line 24) | public Results(T result) { values.Enqueue(() => result); }
method Then (line 25) | public Results<T> Then(T value) { return Then(() => value); }
method Then (line 26) | public Results<T> Then(Func<T> value)
method Next (line 31) | public T Next() { return values.Dequeue()(); }
method InvokesBasketRepositoryFirstOrDefaultAsyncOnceIfAnonymousBasketNotExists (line 34) | [Fact]
method TransferAnonymousBasketItemsWhilePreservingExistingUserBasketItems (line 50) | [Fact]
method RemovesAnonymousBasketAfterUpdatingUserBasket (line 74) | [Fact]
method CreatesNewUserBasketIfNotExists (line 90) | [Fact]
FILE: tests/UnitTests/ApplicationCore/Specifications/BasketWithItemsSpecification.cs
class BasketWithItems (line 10) | public class BasketWithItems
method MatchesBasketWithGivenBasketId (line 15) | [Fact]
method MatchesNoBasketsIfBasketIdNotPresent (line 26) | [Fact]
method MatchesBasketWithGivenBuyerId (line 37) | [Fact]
method MatchesNoBasketsIfBuyerIdNotPresent (line 48) | [Fact]
method GetTestBasketCollection (line 59) | public List<Basket> GetTestBasketCollection()
FILE: tests/UnitTests/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs
class CatalogFilterPaginatedSpecification (line 6) | public class CatalogFilterPaginatedSpecification
method ReturnsAllCatalogItems (line 8) | [Fact]
method Returns2CatalogItemsWithSameBrandAndTypeId (line 19) | [Fact]
method GetTestCollection (line 30) | private List<CatalogItem> GetTestCollection()
FILE: tests/UnitTests/ApplicationCore/Specifications/CatalogFilterSpecification.cs
class CatalogFilterSpecification (line 8) | public class CatalogFilterSpecification
method MatchesExpectedNumberOfItems (line 10) | [Theory]
method GetTestItemCollection (line 27) | public List<CatalogItem> GetTestItemCollection()
FILE: tests/UnitTests/ApplicationCore/Specifications/CatalogItemsSpecification.cs
class CatalogItemsSpecification (line 9) | public class CatalogItemsSpecification
method MatchesSpecificCatalogItem (line 11) | [Fact]
method MatchesAllCatalogItems (line 23) | [Fact]
method GetTestCollection (line 35) | private List<CatalogItem> GetTestCollection()
FILE: tests/UnitTests/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs
class CustomerOrdersWithItemsSpecification (line 8) | public class CustomerOrdersWithItemsSpecification
method ReturnsOrderWithOrderedItem (line 13) | [Fact]
method ReturnsAllOrderWithAllOrderedItem (line 26) | [Fact]
method GetTestCollection (line 42) | public List<Order> GetTestCollection()
FILE: tests/UnitTests/Builders/AddressBuilder.cs
class AddressBuilder (line 5) | public class AddressBuilder
method AddressBuilder (line 14) | public AddressBuilder()
method Build (line 18) | public Address Build()
method WithDefaultValues (line 22) | public Address WithDefaultValues()
FILE: tests/UnitTests/Builders/BasketBuilder.cs
class BasketBuilder (line 6) | public class BasketBuilder
method BasketBuilder (line 13) | public BasketBuilder()
method Build (line 18) | public Basket Build()
method WithNoItems (line 23) | public Basket WithNoItems()
method WithOneBasketItem (line 32) | public Basket WithOneBasketItem()
FILE: tests/UnitTests/Builders/OrderBuilder.cs
class OrderBuilder (line 6) | public class OrderBuilder
method OrderBuilder (line 17) | public OrderBuilder()
method Build (line 23) | public Order Build()
method WithDefaultValues (line 28) | public Order WithDefaultValues()
method WithNoItems (line 36) | public Order WithNoItems()
method WithItems (line 42) | public Order WithItems(List<OrderItem> items)
FILE: tests/UnitTests/MediatorHandlers/OrdersTests/GetMyOrders.cs
class GetMyOrders (line 13) | public class GetMyOrders
method GetMyOrders (line 17) | public GetMyOrders()
method NotReturnNullIfOrdersArePresIent (line 26) | [Fact]
FILE: tests/UnitTests/MediatorHandlers/OrdersTests/GetOrderDetails.cs
class GetOrderDetails (line 14) | public class GetOrderDetails
method GetOrderDetails (line 18) | public GetOrderDetails()
method NotBeNullIfOrderExists (line 28) | [Fact]
FILE: tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateBrandsCacheKey.cs
class GenerateBrandsCacheKey (line 6) | public class GenerateBrandsCacheKey
method ReturnsBrandsCacheKey (line 8) | [Fact]
FILE: tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateCatalogItemCacheKey.cs
class GenerateCatalogItemCacheKey (line 7) | public class GenerateCatalogItemCacheKey
method ReturnsCatalogItemCacheKey (line 9) | [Fact]
FILE: tests/UnitTests/Web/Extensions/CacheHelpersTests/GenerateTypesCacheKey.cs
class GenerateTypesCacheKey (line 6) | public class GenerateTypesCacheKey
method ReturnsTypesCacheKey (line 8) | [Fact]
Condensed preview — 424 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (687K chars).
[
{
"path": ".ado/eshoponweb-cd-aci.yml",
"chars": 1554,
"preview": "#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n\n# trigger: none\nresources:\n pipelines:\n - pipeline: eshoponweb-ci-do"
},
{
"path": ".ado/eshoponweb-cd-webapp-code.yml",
"chars": 1781,
"preview": "#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n\n# Trigger CD when CI executed succesfully\nresources:\n pipelines:\n "
},
{
"path": ".ado/eshoponweb-cd-webapp-docker.yml",
"chars": 1438,
"preview": "#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n\nresources:\n repositories:\n - repository: self\n trigger: none\n"
},
{
"path": ".ado/eshoponweb-cd-webapp-dockercompose.yml",
"chars": 2969,
"preview": "#NOT WORKING YET\n\n#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n#locally docker compose works, deploying to Azure Web"
},
{
"path": ".ado/eshoponweb-cd-windows-cm.yml",
"chars": 677,
"preview": "variables:\r\n resource-group: 'AZ400-EWebShop-NAME'\r\n location: 'centralus'\r\n templateFile: 'infra/simple-windows-vm.b"
},
{
"path": ".ado/eshoponweb-ci-docker.yml",
"chars": 1895,
"preview": "# NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n\nresources:\n repositories:\n - repository: self\n trigger: none"
},
{
"path": ".ado/eshoponweb-ci-dockercompose.yml",
"chars": 2434,
"preview": "#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n\n# Docker\n# Build a Docker image\n# https://docs.microsoft.com/azure/dev"
},
{
"path": ".ado/eshoponweb-ci-mend.yml",
"chars": 1208,
"preview": "#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n# trigger:\n# - main\n\nresources:\n repositories:\n - repository: self\n"
},
{
"path": ".ado/eshoponweb-ci-pr.yml",
"chars": 815,
"preview": "\nresources:\n repositories:\n - repository: self\n trigger: none\n\nstages:\n- stage: Build\n displayName: Build .Net"
},
{
"path": ".ado/eshoponweb-ci.yml",
"chars": 1344,
"preview": "#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n# trigger:\n# - main\n\nresources:\n repositories:\n - repository: self\n"
},
{
"path": ".ado/eshoponweb-sonar-ci.yml",
"chars": 1505,
"preview": "#NAME THE PIPELINE SAME AS FILE (WITHOUT \".yml\")\n# trigger:\n# - main\n\nresources:\n repositories:\n - repository: self\n"
},
{
"path": ".dockerignore",
"chars": 74,
"preview": ".dockerignore\n.env\n.git\n.gitignore\n.vs\n.vscode\n*/bin\n*/obj\n**/.toolstarget"
},
{
"path": ".editorconfig",
"chars": 7003,
"preview": "###############################\n# Core EditorConfig Options #\n###############################\nroot = true\n# All files\n"
},
{
"path": ".gitattributes",
"chars": 88,
"preview": "* text=auto eol=lf\n*.{cmd,[cC][mM][dD]} text eol=crlf\n*.{bat,[bB][aA][tT]} text eol=crlf"
},
{
"path": ".github/dependabot.yml",
"chars": 209,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"nuget\"\n directory: \"/\"\n schedule:\n interval: \"monthly\"\n - packag"
},
{
"path": ".github/workflows/dotnetcore.yml",
"chars": 555,
"preview": "name: eShopOnWeb Build and Test\n\n#Triggers (uncomment line below to use it)\n#on: [push, pull_request, workflow_dispatch]"
},
{
"path": ".github/workflows/eshoponweb-cicd.yml",
"chars": 3669,
"preview": "name: eShopOnWeb Build and Test\n\n#Triggers (uncomment line below to use it)\n#on: [push, workflow_dispatch]\n\n#Environment"
},
{
"path": ".github/workflows/richnav.yml",
"chars": 530,
"preview": "name: eShopOnWeb - Code Index\n\non: workflow_dispatch\n\njobs:\n build:\n runs-on: windows-latest\n\n steps:\n - use"
},
{
"path": ".gitignore",
"chars": 4545,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
},
{
"path": ".vscode/extensions.json",
"chars": 264,
"preview": "{\n \"recommendations\": [\n \"ms-dotnettools.csharp\",\n \"formulahendry.dotnet-test-explorer\",\n \"ms-vscode.v"
},
{
"path": ".vscode/launch.json",
"chars": 1426,
"preview": "{\n // Use IntelliSense to find out which attributes exist for C# debugging\n // Use hover for the description of the "
},
{
"path": ".vscode/tasks.json",
"chars": 1220,
"preview": "{\n \"version\": \"2.0.0\",\n \"tasks\": [\n {\n \"label\": \"build\",\n \"command\": \"dotnet\",\n "
},
{
"path": "CodeCoverage.runsettings",
"chars": 5114,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- File name extension must be .runsettings -->\n<RunSettings>\n <DataCollectio"
},
{
"path": "Directory.Packages.props",
"chars": 5273,
"preview": "<Project>\n <PropertyGroup>\n <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>\n <TargetFramewo"
},
{
"path": "LICENSE",
"chars": 1104,
"preview": "MIT License\n\n\n\nCopyright (c) .NET Foundation and Contributors\n\n\n\nPermission is hereby granted, free of charge, to any pe"
},
{
"path": "MTT-Notes.md",
"chars": 1521,
"preview": "# App modified\nFor architecture simplicity (and problems related to deploy SQL on free subscriptions), inmemory database"
},
{
"path": "README.md",
"chars": 8948,
"preview": "[]"
},
{
"path": "azure.yaml",
"chars": 98,
"preview": "name: eShopOnWeb\nservices:\n web:\n project: ./src/Web\n language: csharp\n host: appservice"
},
{
"path": "docker-compose-webapp.yml",
"chars": 530,
"preview": "version: '3.4'\n\nservices:\n eshopwebmvc:\n image: __acr-login-server__/eshopwebmvc\n build:\n context: .\n d"
},
{
"path": "docker-compose.dcproj",
"chars": 745,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" Sdk=\"Microsoft.Docker.Sdk\" DefaultTargets=\"Build\">\n "
},
{
"path": "docker-compose.override.yml",
"chars": 537,
"preview": "version: '3.4'\nservices:\n eshopwebmvc:\n environment:\n - ASPNETCORE_ENVIRONMENT=Docker\n - ASPNETCORE_URLS=http:"
},
{
"path": "docker-compose.yml",
"chars": 512,
"preview": "services:\n eshopwebmvc:\n image: ${DOCKER_REGISTRY-}eshopwebmvc\n build:\n context: .\n dockerfile: src/Web"
},
{
"path": "eShopOnWeb.sln",
"chars": 7743,
"preview": "Microsoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.0.31903."
},
{
"path": "global.json",
"chars": 78,
"preview": "{\n \"sdk\": {\n \"version\": \"8.0.x\",\n \"rollForward\": \"latestFeature\"\n }\n}\n"
},
{
"path": "infra/abbreviations.json",
"chars": 5337,
"preview": "{\n \"analysisServicesServers\": \"as\",\n \"apiManagementService\": \"apim-\",\n \"appConfigurationConfigurationStores\": \""
},
{
"path": "infra/aci.bicep",
"chars": 2422,
"preview": "@description('Name for the container group')\nparam name string = 'eshopcontainer'\n\n@description('Location for all resour"
},
{
"path": "infra/acr.bicep",
"chars": 534,
"preview": "@description('Generate a Suffix based on the Resource Group ID')\nparam suffix string = uniqueString(resourceGroup().id)\n"
},
{
"path": "infra/core/database/sqlserver/sqlserver.bicep",
"chars": 3384,
"preview": "param name string\nparam location string = resourceGroup().location\nparam tags object = {}\n\nparam appUser string = 'appUs"
},
{
"path": "infra/core/host/appservice.bicep",
"chars": 3461,
"preview": "param name string\nparam location string = resourceGroup().location\nparam tags object = {}\n\n// Reference Properties\nparam"
},
{
"path": "infra/core/host/appserviceplan.bicep",
"chars": 380,
"preview": "param name string\nparam location string = resourceGroup().location\nparam tags object = {}\n\nparam kind string = ''\nparam "
},
{
"path": "infra/core/security/keyvault-access.bicep",
"chars": 514,
"preview": "param name string = 'add'\n\nparam keyVaultName string\nparam permissions object = { secrets: [ 'get', 'list' ] }\nparam pri"
},
{
"path": "infra/core/security/keyvault.bicep",
"chars": 618,
"preview": "param name string\nparam location string = resourceGroup().location\nparam tags object = {}\n\nparam principalId string = ''"
},
{
"path": "infra/main.bicep",
"chars": 4791,
"preview": "targetScope = 'subscription'\n\n@minLength(1)\n@maxLength(64)\n@description('Name of the the environment which is used to ge"
},
{
"path": "infra/main.parameters.json",
"chars": 579,
"preview": "{\n \"$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#\",\n \"contentVersion\": "
},
{
"path": "infra/simple-windows-vm.bicep",
"chars": 6672,
"preview": "@description('Username for the Virtual Machine.')\nparam adminUsername string = 'Student'\n\n@description('Password for the"
},
{
"path": "infra/webapp-docker.bicep",
"chars": 1162,
"preview": "@description('Generate a Suffix based on the Resource Group ID')\nparam suffix string = uniqueString(resourceGroup().id)\n"
},
{
"path": "infra/webapp-to-acr-roleassignment.bicep",
"chars": 928,
"preview": "@description('Generate a Suffix based on the Resource Group ID')\nparam suffix string = uniqueString(resourceGroup().id)\n"
},
{
"path": "infra/webapp.bicep",
"chars": 910,
"preview": "param webAppName string // = uniqueString(resourceGroup().id) // unique String gets created from az cli instructions\npar"
},
{
"path": "src/ApplicationCore/ApplicationCore.csproj",
"chars": 572,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\t\n\t\t<RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace"
},
{
"path": "src/ApplicationCore/CatalogSettings.cs",
"chars": 113,
"preview": "namespace Microsoft.eShopWeb;\n\npublic class CatalogSettings\n{\n public string? CatalogBaseUrl { get; set; }\n}\n"
},
{
"path": "src/ApplicationCore/Constants/AuthorizationConstants.cs",
"chars": 425,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Constants;\n\npublic class AuthorizationConstants\n{\n public const string "
},
{
"path": "src/ApplicationCore/Entities/BaseEntity.cs",
"chars": 319,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Entities;\n\n// This can easily be modified to be BaseEntity<T> and public T"
},
{
"path": "src/ApplicationCore/Entities/BasketAggregate/Basket.cs",
"chars": 1164,
"preview": "using System.Collections.Generic;\nusing System.Linq;\nusing Ardalis.GuardClauses;\nusing Microsoft.eShopWeb.ApplicationCo"
},
{
"path": "src/ApplicationCore/Entities/BasketAggregate/BasketItem.cs",
"chars": 855,
"preview": "using Ardalis.GuardClauses;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;\n\npublic class Baske"
},
{
"path": "src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs",
"chars": 702,
"preview": "using System.Collections.Generic;\nusing Ardalis.GuardClauses;\nusing Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\nnam"
},
{
"path": "src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs",
"chars": 332,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate;\n\npublic class PaymentMethod : BaseEntity\n{\n pu"
},
{
"path": "src/ApplicationCore/Entities/CatalogBrand.cs",
"chars": 289,
"preview": "using Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Entities;\n\npublic cl"
},
{
"path": "src/ApplicationCore/Entities/CatalogItem.cs",
"chars": 2395,
"preview": "using System;\nusing Ardalis.GuardClauses;\nusing Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\nnamespace Microsoft.eSh"
},
{
"path": "src/ApplicationCore/Entities/CatalogType.cs",
"chars": 283,
"preview": "using Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Entities;\n\npublic cl"
},
{
"path": "src/ApplicationCore/Entities/EshopDiagram.cd",
"chars": 1389,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ClassDiagram MajorVersion=\"1\" MinorVersion=\"1\">\n <Class Name=\"Microsoft.eShopW"
},
{
"path": "src/ApplicationCore/Entities/OrderAggregate/Address.cs",
"chars": 673,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;\n\npublic class Address // ValueObject\n{\n public"
},
{
"path": "src/ApplicationCore/Entities/OrderAggregate/CatalogItemOrdered.cs",
"chars": 1039,
"preview": "using Ardalis.GuardClauses;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;\n\n/// <summary>\n/// R"
},
{
"path": "src/ApplicationCore/Entities/OrderAggregate/Order.cs",
"chars": 1791,
"preview": "using System;\nusing System.Collections.Generic;\nusing Ardalis.GuardClauses;\nusing Microsoft.eShopWeb.ApplicationCore.In"
},
{
"path": "src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs",
"chars": 550,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;\n\npublic class OrderItem : BaseEntity\n{\n public"
},
{
"path": "src/ApplicationCore/Exceptions/BasketNotFoundException.cs",
"chars": 234,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Exceptions;\n\npublic class BasketNotFoundException : Excepti"
},
{
"path": "src/ApplicationCore/Exceptions/DuplicateException.cs",
"chars": 198,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Exceptions;\n\npublic class DuplicateException : Exception\n{\n"
},
{
"path": "src/ApplicationCore/Exceptions/EmptyBasketOnCheckoutException.cs",
"chars": 464,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Exceptions;\n\npublic class EmptyBasketOnCheckoutException : "
},
{
"path": "src/ApplicationCore/Extensions/GuardExtensions.cs",
"chars": 466,
"preview": "using System.Collections.Generic;\nusing System.Linq;\nusing Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;"
},
{
"path": "src/ApplicationCore/Extensions/JsonExtensions.cs",
"chars": 464,
"preview": "using System.Text.Json;\n\nnamespace Microsoft.eShopWeb;\n\npublic static class JsonExtensions\n{\n private static readonl"
},
{
"path": "src/ApplicationCore/Interfaces/IAggregateRoot.cs",
"chars": 95,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\npublic interface IAggregateRoot\n{ }\n"
},
{
"path": "src/ApplicationCore/Interfaces/IAppLogger.cs",
"chars": 370,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\n/// <summary>\n/// This type eliminates the need to depend dir"
},
{
"path": "src/ApplicationCore/Interfaces/IBasketQueryService.cs",
"chars": 280,
"preview": "using System.Threading.Tasks;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\n/// <summary>\n/// Specific que"
},
{
"path": "src/ApplicationCore/Interfaces/IBasketService.cs",
"chars": 551,
"preview": "using System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Ardalis.Result;\nusing Microsoft.eShopWeb.Applicat"
},
{
"path": "src/ApplicationCore/Interfaces/IEmailSender.cs",
"chars": 195,
"preview": "using System.Threading.Tasks;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\npublic interface IEmailSender\n"
},
{
"path": "src/ApplicationCore/Interfaces/IOrderService.cs",
"chars": 257,
"preview": "using System.Threading.Tasks;\nusing Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;\n\nnamespace Microsoft.eS"
},
{
"path": "src/ApplicationCore/Interfaces/IReadRepository.cs",
"chars": 186,
"preview": "using Ardalis.Specification;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\npublic interface IReadRepositor"
},
{
"path": "src/ApplicationCore/Interfaces/IRepository.cs",
"chars": 178,
"preview": "using Ardalis.Specification;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\npublic interface IRepository<T>"
},
{
"path": "src/ApplicationCore/Interfaces/ITokenClaimsService.cs",
"chars": 180,
"preview": "using System.Threading.Tasks;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\npublic interface ITokenClaimsS"
},
{
"path": "src/ApplicationCore/Interfaces/IUriComposer.cs",
"chars": 139,
"preview": "namespace Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\npublic interface IUriComposer\n{\n string ComposePicUri(stri"
},
{
"path": "src/ApplicationCore/Services/BasketService.cs",
"chars": 3171,
"preview": "using System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Ardalis.GuardClauses;\nusing Ardalis.Result;\nusing"
},
{
"path": "src/ApplicationCore/Services/OrderService.cs",
"chars": 2207,
"preview": "using System.Linq;\nusing System.Threading.Tasks;\nusing Ardalis.GuardClauses;\nusing Microsoft.eShopWeb.ApplicationCore.E"
},
{
"path": "src/ApplicationCore/Services/UriComposer.cs",
"chars": 477,
"preview": "using Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\nnamespace Microsoft.eShopWeb.ApplicationCore.Services;\n\npublic cl"
},
{
"path": "src/ApplicationCore/Specifications/BasketWithItemsSpecification.cs",
"chars": 560,
"preview": "using Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;\n\nnamespace Microsoft.eS"
},
{
"path": "src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs",
"chars": 614,
"preview": "using Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities;\n\nnamespace Microsoft.eShopWeb.Applicati"
},
{
"path": "src/ApplicationCore/Specifications/CatalogFilterSpecification.cs",
"chars": 435,
"preview": "using Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities;\n\nnamespace Microsoft.eShopWeb.Applicati"
},
{
"path": "src/ApplicationCore/Specifications/CatalogItemNameSpecification.cs",
"chars": 354,
"preview": "using Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities;\n\nnamespace Microsoft.eShopWeb.Applicati"
},
{
"path": "src/ApplicationCore/Specifications/CatalogItemsSpecification.cs",
"chars": 362,
"preview": "using System;\nusing System.Linq;\nusing Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities;\n\nnames"
},
{
"path": "src/ApplicationCore/Specifications/CustomerOrdersSpecification.cs",
"chars": 382,
"preview": "using Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;\n\nnamespace Microsoft.eSh"
},
{
"path": "src/ApplicationCore/Specifications/CustomerOrdersWithItemsSpecification.cs",
"chars": 449,
"preview": "using Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;\n\nnamespace Microsoft.eSh"
},
{
"path": "src/ApplicationCore/Specifications/OrderWithItemsByIdSpec.cs",
"chars": 430,
"preview": "using Ardalis.Specification;\nusing Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;\n\nnamespace Microsoft.eSh"
},
{
"path": "src/BlazorAdmin/App.razor",
"chars": 1079,
"preview": "<CascadingAuthenticationState>\n <Router AppAssembly=\"@typeof(Program).Assembly\">\n <Found Context=\"routeData\">\n"
},
{
"path": "src/BlazorAdmin/BlazorAdmin.csproj",
"chars": 1250,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk.BlazorWebAssembly\">\t\n\t<ItemGroup>\n\t\t<PackageReference Include=\"Blazored.LocalStorage\" /"
},
{
"path": "src/BlazorAdmin/CustomAuthStateProvider.cs",
"chars": 2530,
"preview": "using System;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Net.Http.Json;\nusing System.Security.C"
},
{
"path": "src/BlazorAdmin/Helpers/BlazorComponent.cs",
"chars": 492,
"preview": "using Microsoft.AspNetCore.Components;\n\nnamespace BlazorAdmin.Helpers;\n\npublic class BlazorComponent : ComponentBase\n{\n"
},
{
"path": "src/BlazorAdmin/Helpers/BlazorLayoutComponent.cs",
"chars": 503,
"preview": "using Microsoft.AspNetCore.Components;\n\nnamespace BlazorAdmin.Helpers;\n\npublic class BlazorLayoutComponent : LayoutComp"
},
{
"path": "src/BlazorAdmin/Helpers/RefreshBroadcast.cs",
"chars": 476,
"preview": "using System;\n\nnamespace BlazorAdmin.Helpers;\n\ninternal sealed class RefreshBroadcast\n{\n private static readonly Laz"
},
{
"path": "src/BlazorAdmin/Helpers/ToastComponent.cs",
"chars": 1988,
"preview": "using System;\nusing BlazorAdmin.Services;\nusing Microsoft.AspNetCore.Components;\n\nnamespace BlazorAdmin.Helpers;\n\npubli"
},
{
"path": "src/BlazorAdmin/JavaScript/Cookies.cs",
"chars": 550,
"preview": "using System.Threading.Tasks;\nusing Microsoft.JSInterop;\n\nnamespace BlazorAdmin.JavaScript;\n\npublic class Cookies\n{\n "
},
{
"path": "src/BlazorAdmin/JavaScript/Css.cs",
"chars": 530,
"preview": "using System.Threading.Tasks;\nusing Microsoft.JSInterop;\n\nnamespace BlazorAdmin.JavaScript;\n\npublic class Css\n{\n pri"
},
{
"path": "src/BlazorAdmin/JavaScript/JSInteropConstants.cs",
"chars": 374,
"preview": "namespace BlazorAdmin.JavaScript;\n\npublic static class JSInteropConstants\n{\n public static string DeleteCookie => \"d"
},
{
"path": "src/BlazorAdmin/JavaScript/Route.cs",
"chars": 391,
"preview": "using System.Threading.Tasks;\nusing Microsoft.JSInterop;\n\nnamespace BlazorAdmin.JavaScript;\n\npublic class Route\n{\n p"
},
{
"path": "src/BlazorAdmin/Pages/CatalogItemPage/Create.razor",
"chars": 7013,
"preview": "@inject ILogger<Create> Logger\n@inject IJSRuntime JSRuntime\n@inject ICatalogItemService CatalogItemService\n\n@inherits B"
},
{
"path": "src/BlazorAdmin/Pages/CatalogItemPage/Delete.razor",
"chars": 4342,
"preview": "@inject ILogger<Delete> Logger\n@inject IJSRuntime JSRuntime\n@inject ICatalogItemService CatalogItemService\n\n@inherits B"
},
{
"path": "src/BlazorAdmin/Pages/CatalogItemPage/Details.razor",
"chars": 4208,
"preview": "@inject ILogger<Details> Logger\n@inject IJSRuntime JSRuntime\n@inject ICatalogItemService CatalogItemService\n\n@inherits "
},
{
"path": "src/BlazorAdmin/Pages/CatalogItemPage/Edit.razor",
"chars": 6952,
"preview": "@inject ILogger<Edit> Logger\n@inject IJSRuntime JSRuntime\n@inject ICatalogItemService CatalogItemService\n\n@inherits Bla"
},
{
"path": "src/BlazorAdmin/Pages/CatalogItemPage/List.razor",
"chars": 2479,
"preview": "@page \"/admin\"\n@attribute [Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS)]\n@inherits Blaz"
},
{
"path": "src/BlazorAdmin/Pages/CatalogItemPage/List.razor.cs",
"chars": 1936,
"preview": "using System.Collections.Generic;\nusing System.Threading.Tasks;\nusing BlazorAdmin.Helpers;\nusing BlazorShared.Interface"
},
{
"path": "src/BlazorAdmin/Pages/Logout.razor",
"chars": 335,
"preview": "@page \"/logout\"\n@inject IJSRuntime JSRuntime\n@inject HttpClient HttpClient\n@inherits BlazorAdmin.Helpers.BlazorComponen"
},
{
"path": "src/BlazorAdmin/Program.cs",
"chars": 1836,
"preview": "using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\nusing BlazorAdmin;\nusing BlazorAdmin.Services;\nusing"
},
{
"path": "src/BlazorAdmin/Properties/launchSettings.json",
"chars": 956,
"preview": "{\n \"iisSettings\": {\n \"windowsAuthentication\": false,\n \"anonymousAuthentication\": true,\n \"iisExpress\": {\n "
},
{
"path": "src/BlazorAdmin/Services/CacheEntry.cs",
"chars": 280,
"preview": "using System;\n\nnamespace BlazorAdmin.Services;\n\npublic class CacheEntry<T>\n{\n public CacheEntry(T item)\n {\n "
},
{
"path": "src/BlazorAdmin/Services/CachedCatalogItemServiceDecorator.cs",
"chars": 3646,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Blazored.LocalSt"
},
{
"path": "src/BlazorAdmin/Services/CachedCatalogLookupDataServiceDecorator .cs",
"chars": 1976,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Blazored.LocalStorage;\nusing Blazor"
},
{
"path": "src/BlazorAdmin/Services/CatalogItemService.cs",
"chars": 3829,
"preview": "using System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing BlazorShared.Interfaces;\nusing"
},
{
"path": "src/BlazorAdmin/Services/CatalogLookupDataService.cs",
"chars": 1453,
"preview": "using System.Collections.Generic;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Net.Http.Json;\nusing System.Re"
},
{
"path": "src/BlazorAdmin/Services/HttpService.cs",
"chars": 2795,
"preview": "using System.Net.Http;\nusing System.Text;\nusing System.Text.Json;\nusing System.Threading.Tasks;\nusing BlazorShared;\nusi"
},
{
"path": "src/BlazorAdmin/Services/ToastService.cs",
"chars": 1077,
"preview": "using System;\nusing System.Timers;\n\nnamespace BlazorAdmin.Services;\n\npublic enum ToastLevel\n{\n Info,\n Success,\n "
},
{
"path": "src/BlazorAdmin/ServicesConfiguration.cs",
"chars": 944,
"preview": "using BlazorAdmin.Services;\nusing BlazorShared.Interfaces;\nusing BlazorShared.Models;\nusing Microsoft.Extensions.Depend"
},
{
"path": "src/BlazorAdmin/Shared/CustomInputSelect.cs",
"chars": 1139,
"preview": "using Microsoft.AspNetCore.Components.Forms;\n\nnamespace BlazorAdmin.Shared;\n\n/// <summary>\n/// This is needed until 5.0"
},
{
"path": "src/BlazorAdmin/Shared/MainLayout.razor",
"chars": 1093,
"preview": "@inject AuthenticationStateProvider AuthStateProvider\n@inject IJSRuntime JSRuntime\n\n@inherits BlazorAdmin.Helpers.Blazo"
},
{
"path": "src/BlazorAdmin/Shared/NavMenu.razor",
"chars": 1516,
"preview": "@inherits BlazorAdmin.Helpers.BlazorComponent\n<div class=\"top-row pl-4 navbar navbar-dark\">\n <a class=\"navbar-brand\""
},
{
"path": "src/BlazorAdmin/Shared/RedirectToLogin.razor",
"chars": 386,
"preview": "@using System.Web;\n\n@inject NavigationManager Navigation\n@inject IJSRuntime JsRuntime\n\n@code {\n protected override v"
},
{
"path": "src/BlazorAdmin/Shared/Spinner.razor",
"chars": 176,
"preview": "\n@inherits BlazorAdmin.Helpers.BlazorComponent\n\n@namespace BlazorAdmin.Shared\n\n<div class=\"spinner-border\" role=\"statu"
},
{
"path": "src/BlazorAdmin/Shared/Toast.razor",
"chars": 356,
"preview": "@inherits BlazorAdmin.Helpers.ToastComponent\n\n@namespace BlazorAdmin.Shared\n\n<div class=\"toast @(IsVisible ? \"toast-vis"
},
{
"path": "src/BlazorAdmin/_Imports.razor",
"chars": 620,
"preview": "@using System.Net.Http\n@using System.Net.Http.Json\n@using Microsoft.AspNetCore.Authorization;\n@using Microsoft.AspNetCo"
},
{
"path": "src/BlazorAdmin/wwwroot/appsettings.Development.json",
"chars": 271,
"preview": "{\n \"baseUrls\": {\n \"apiBase\": \"https://localhost:5099/api/\",\n \"webBase\": \"https://localhost:44315/\"\n },\n \"Loggin"
},
{
"path": "src/BlazorAdmin/wwwroot/appsettings.Docker.json",
"chars": 119,
"preview": "{\n \"baseUrls\": {\n \"apiBase\": \"http://localhost:5200/api/\",\n \"webBase\": \"http://host.docker.internal:5106/\"\n }\n}"
},
{
"path": "src/BlazorAdmin/wwwroot/appsettings.json",
"chars": 271,
"preview": "{\n \"baseUrls\": {\n \"apiBase\": \"https://localhost:5099/api/\",\n \"webBase\": \"https://localhost:44315/\"\n },\n \"Loggin"
},
{
"path": "src/BlazorAdmin/wwwroot/css/admin.css",
"chars": 4147,
"preview": "@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');\n\nhtml, body {\n font-family: 'Helvetica Neue', Hel"
},
{
"path": "src/BlazorAdmin/wwwroot/css/open-iconic/FONT-LICENSE",
"chars": 4017,
"preview": "SIL OPEN FONT LICENSE Version 1.1\n\nCopyright (c) 2014 Waybury\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to "
},
{
"path": "src/BlazorAdmin/wwwroot/css/open-iconic/ICON-LICENSE",
"chars": 1073,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Waybury\n\nPermission is hereby granted, free of charge, to any person obtaining"
},
{
"path": "src/BlazorAdmin/wwwroot/css/open-iconic/README.md",
"chars": 3495,
"preview": "[Open Iconic v1.1.1](http://useiconic.com/open)\n===========\n\n### Open Iconic is the open source sibling of [Iconic](http"
},
{
"path": "src/BlazorShared/Attributes/EndpointAttribute.cs",
"chars": 136,
"preview": "using System;\n\nnamespace BlazorShared.Attributes;\n\npublic class EndpointAttribute : Attribute\n{\n public string Name "
},
{
"path": "src/BlazorShared/Authorization/ClaimValue.cs",
"chars": 286,
"preview": "namespace BlazorShared.Authorization;\n\npublic class ClaimValue\n{\n public ClaimValue()\n {\n }\n\n public ClaimV"
},
{
"path": "src/BlazorShared/Authorization/Constants.cs",
"chars": 179,
"preview": "namespace BlazorShared.Authorization;\n\npublic static class Constants\n{\n public static class Roles\n {\n publ"
},
{
"path": "src/BlazorShared/Authorization/UserInfo.cs",
"chars": 397,
"preview": "using System.Collections.Generic;\n\nnamespace BlazorShared.Authorization;\n\npublic class UserInfo\n{\n public static rea"
},
{
"path": "src/BlazorShared/BaseUrlConfiguration.cs",
"chars": 195,
"preview": "namespace BlazorShared;\n\npublic class BaseUrlConfiguration\n{\n public const string CONFIG_NAME = \"baseUrls\";\n\n pub"
},
{
"path": "src/BlazorShared/BlazorShared.csproj",
"chars": 314,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup> \n <RootNamespace>BlazorShared</RootNamespace>\n <AssemblyNam"
},
{
"path": "src/BlazorShared/Interfaces/ICatalogItemService.cs",
"chars": 452,
"preview": "using System.Collections.Generic;\nusing System.Threading.Tasks;\nusing BlazorShared.Models;\n\nnamespace BlazorShared.Inte"
},
{
"path": "src/BlazorShared/Interfaces/ICatalogLookupDataService.cs",
"chars": 256,
"preview": "using System.Collections.Generic;\nusing System.Threading.Tasks;\nusing BlazorShared.Models;\n\nnamespace BlazorShared.Inte"
},
{
"path": "src/BlazorShared/Interfaces/ILookupDataResponse.cs",
"chars": 225,
"preview": "using System.Collections.Generic;\nusing BlazorShared.Models;\n\nnamespace BlazorShared.Interfaces;\n\npublic interface ILoo"
},
{
"path": "src/BlazorShared/Models/CatalogBrand.cs",
"chars": 144,
"preview": "using BlazorShared.Attributes;\n\nnamespace BlazorShared.Models;\n\n[Endpoint(Name = \"catalog-brands\")]\npublic class Catalo"
},
{
"path": "src/BlazorShared/Models/CatalogBrandResponse.cs",
"chars": 328,
"preview": "using System.Collections.Generic;\nusing System.Text.Json.Serialization;\nusing BlazorShared.Interfaces;\n\nnamespace Blazo"
},
{
"path": "src/BlazorShared/Models/CatalogItem.cs",
"chars": 2624,
"preview": "using System;\nusing System.ComponentModel.DataAnnotations;\nusing System.IO;\nusing System.Threading.Tasks;\nusing BlazorI"
},
{
"path": "src/BlazorShared/Models/CatalogType.cs",
"chars": 142,
"preview": "using BlazorShared.Attributes;\n\nnamespace BlazorShared.Models;\n\n[Endpoint(Name = \"catalog-types\")]\npublic class Catalog"
},
{
"path": "src/BlazorShared/Models/CatalogTypeResponse.cs",
"chars": 324,
"preview": "using System.Collections.Generic;\nusing System.Text.Json.Serialization;\nusing BlazorShared.Interfaces;\n\nnamespace Blazo"
},
{
"path": "src/BlazorShared/Models/CreateCatalogItemRequest.cs",
"chars": 891,
"preview": "using System.ComponentModel.DataAnnotations;\n\nnamespace BlazorShared.Models;\n\npublic class CreateCatalogItemRequest\n{\n "
},
{
"path": "src/BlazorShared/Models/CreateCatalogItemResponse.cs",
"chars": 146,
"preview": "namespace BlazorShared.Models;\n\npublic class CreateCatalogItemResponse\n{\n public CatalogItem CatalogItem { get; set;"
},
{
"path": "src/BlazorShared/Models/DeleteCatalogItemResponse.cs",
"chars": 128,
"preview": "namespace BlazorShared.Models;\n\npublic class DeleteCatalogItemResponse\n{\n public string Status { get; set; } = \"Dele"
},
{
"path": "src/BlazorShared/Models/EditCatalogItemResponse.cs",
"chars": 142,
"preview": "namespace BlazorShared.Models;\n\npublic class EditCatalogItemResult\n{\n public CatalogItem CatalogItem { get; set; } ="
},
{
"path": "src/BlazorShared/Models/ErrorDetails.cs",
"chars": 265,
"preview": "using System.Text.Json;\n\nnamespace BlazorShared.Models;\n\npublic class ErrorDetails\n{\n public int StatusCode { get; s"
},
{
"path": "src/BlazorShared/Models/LookupData.cs",
"chars": 139,
"preview": "namespace BlazorShared.Models;\n\npublic abstract class LookupData\n{\n public int Id { get; set; }\n public string Na"
},
{
"path": "src/BlazorShared/Models/PagedCatalogItemResponse.cs",
"chars": 237,
"preview": "using System.Collections.Generic;\n\nnamespace BlazorShared.Models;\n\npublic class PagedCatalogItemResponse\n{\n public L"
},
{
"path": "src/Infrastructure/Data/CatalogContext.cs",
"chars": 1071,
"preview": "using System.Reflection;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.eShopWeb.ApplicationCore.Entities;\nusing "
},
{
"path": "src/Infrastructure/Data/CatalogContextSeed.cs",
"chars": 4089,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing Microsoft.EntityFrameworkCore;\nusin"
},
{
"path": "src/Infrastructure/Data/Config/BasketConfiguration.cs",
"chars": 613,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.eShopWeb.Ap"
},
{
"path": "src/Infrastructure/Data/Config/BasketItemConfiguration.cs",
"chars": 496,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.eShopWeb.Ap"
},
{
"path": "src/Infrastructure/Data/Config/CatalogBrandConfiguration.cs",
"chars": 610,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.eShopWeb.Ap"
},
{
"path": "src/Infrastructure/Data/Config/CatalogItemConfiguration.cs",
"chars": 1046,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.eShopWeb.Ap"
},
{
"path": "src/Infrastructure/Data/Config/CatalogTypeConfiguration.cs",
"chars": 605,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.eShopWeb.Ap"
},
{
"path": "src/Infrastructure/Data/Config/OrderConfiguration.cs",
"chars": 1270,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.eShopWeb.Ap"
},
{
"path": "src/Infrastructure/Data/Config/OrderItemConfiguration.cs",
"chars": 707,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing Microsoft.eShopWeb.Ap"
},
{
"path": "src/Infrastructure/Data/EfRepository.cs",
"chars": 356,
"preview": "using Ardalis.Specification.EntityFrameworkCore;\nusing Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\nnamespace Micros"
},
{
"path": "src/Infrastructure/Data/FileItem.cs",
"chars": 311,
"preview": "namespace Microsoft.eShopWeb.Infrastructure.Data;\n\npublic class FileItem\n{\n public string? FileName { get; set; }\n "
},
{
"path": "src/Infrastructure/Data/Migrations/20201202111507_InitialModel.Designer.cs",
"chars": 11410,
"preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
},
{
"path": "src/Infrastructure/Data/Migrations/20201202111507_InitialModel.cs",
"chars": 8184,
"preview": "using System;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\nnamespace Microsoft.eShopWeb.Infrastructure.Data.Migrati"
},
{
"path": "src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.Designer.cs",
"chars": 12526,
"preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
},
{
"path": "src/Infrastructure/Data/Migrations/20211026175614_FixBuyerId.cs",
"chars": 1544,
"preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\nnamespace Microsoft.eShopWeb.Infrastructure.Data.Migrations;\n\npublic p"
},
{
"path": "src/Infrastructure/Data/Migrations/20211231093753_FixShipToAddress.Designer.cs",
"chars": 11809,
"preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
},
{
"path": "src/Infrastructure/Data/Migrations/20211231093753_FixShipToAddress.cs",
"chars": 3484,
"preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\n#nullable disable\n\nnamespace Microsoft.eShopWeb.Infrastructure.Data.Mi"
},
{
"path": "src/Infrastructure/Data/Migrations/CatalogContextModelSnapshot.cs",
"chars": 11731,
"preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
},
{
"path": "src/Infrastructure/Data/Queries/BasketQueryService.cs",
"chars": 889,
"preview": "using System.Linq;\nusing System.Threading.Tasks;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.eShopWeb.Applicat"
},
{
"path": "src/Infrastructure/Dependencies.cs",
"chars": 1513,
"preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.eShopWeb.Infrastructure.Data;\nusing Microsoft.eShopWeb.Infrastruct"
},
{
"path": "src/Infrastructure/Identity/AppIdentityDbContext.cs",
"chars": 708,
"preview": "using Microsoft.AspNetCore.Identity.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore;\n\n\nnamespace Microsoft.eSh"
},
{
"path": "src/Infrastructure/Identity/AppIdentityDbContextSeed.cs",
"chars": 1364,
"preview": "using System.Threading.Tasks;\nusing Microsoft.AspNetCore.Identity;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft"
},
{
"path": "src/Infrastructure/Identity/ApplicationUser.cs",
"chars": 142,
"preview": "using Microsoft.AspNetCore.Identity;\n\nnamespace Microsoft.eShopWeb.Infrastructure.Identity;\n\npublic class ApplicationUs"
},
{
"path": "src/Infrastructure/Identity/IdentityTokenClaimService.cs",
"chars": 1689,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.IdentityModel.Tokens.Jwt;\nusing System.Security.Claims;\nus"
},
{
"path": "src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.Designer.cs",
"chars": 10220,
"preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
},
{
"path": "src/Infrastructure/Identity/Migrations/20201202111612_InitialIdentityModel.cs",
"chars": 9334,
"preview": "using System;\nusing Microsoft.EntityFrameworkCore.Migrations;\n\nnamespace Microsoft.eShopWeb.Infrastructure.Identity.Mig"
},
{
"path": "src/Infrastructure/Identity/Migrations/AppIdentityDbContextModelSnapshot.cs",
"chars": 10140,
"preview": "// <auto-generated />\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Infrastruc"
},
{
"path": "src/Infrastructure/Identity/UserNotFoundException.cs",
"chars": 235,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.Infrastructure.Identity;\n\npublic class UserNotFoundException : Exception\n{\n"
},
{
"path": "src/Infrastructure/Infrastructure.csproj",
"chars": 746,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n\t<PropertyGroup>\t\t\n\t\t<RootNamespace>Microsoft.eShopWeb.Infrastructure</RootNamespace"
},
{
"path": "src/Infrastructure/Logging/LoggerAdapter.cs",
"chars": 604,
"preview": "using Microsoft.eShopWeb.ApplicationCore.Interfaces;\nusing Microsoft.Extensions.Logging;\n\nnamespace Microsoft.eShopWeb."
},
{
"path": "src/Infrastructure/Services/EmailSender.cs",
"chars": 570,
"preview": "using System.Threading.Tasks;\nusing Microsoft.eShopWeb.ApplicationCore.Interfaces;\n\nnamespace Microsoft.eShopWeb.Infras"
},
{
"path": "src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateRequest.cs",
"chars": 189,
"preview": "namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;\n\npublic class AuthenticateRequest : BaseRequest\n{\n public stri"
},
{
"path": "src/PublicApi/AuthEndpoints/AuthenticateEndpoint.AuthenticateResponse.cs",
"chars": 574,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;\n\npublic class AuthenticateResponse : BaseResponse\n"
},
{
"path": "src/PublicApi/AuthEndpoints/AuthenticateEndpoint.ClaimValue.cs",
"chars": 334,
"preview": "namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;\n\npublic class ClaimValue\n{\n public ClaimValue()\n {\n }\n\n "
},
{
"path": "src/PublicApi/AuthEndpoints/AuthenticateEndpoint.UserInfo.cs",
"chars": 433,
"preview": "using System.Collections.Generic;\n\nnamespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;\n\npublic class UserInfo\n{\n p"
},
{
"path": "src/PublicApi/AuthEndpoints/AuthenticateEndpoint.cs",
"chars": 2228,
"preview": "using System.Threading;\nusing System.Threading.Tasks;\nusing Ardalis.ApiEndpoints;\nusing Microsoft.AspNetCore.Identity;\n"
},
{
"path": "src/PublicApi/BaseMessage.cs",
"chars": 342,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.PublicApi;\n\n/// <summary>\n/// Base class used by API requests\n/// </summary"
},
{
"path": "src/PublicApi/BaseRequest.cs",
"chars": 159,
"preview": "namespace Microsoft.eShopWeb.PublicApi;\n\n/// <summary>\n/// Base class used by API requests\n/// </summary>\npublic abstra"
},
{
"path": "src/PublicApi/BaseResponse.cs",
"chars": 325,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.PublicApi;\n\n/// <summary>\n/// Base class used by API responses\n/// </summar"
},
{
"path": "src/PublicApi/CatalogBrandEndpoints/CatalogBrandDto.cs",
"chars": 166,
"preview": "namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;\n\npublic class CatalogBrandDto\n{\n public int Id { get; "
},
{
"path": "src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.ListCatalogBrandsResponse.cs",
"chars": 407,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;\n\npublic "
},
{
"path": "src/PublicApi/CatalogBrandEndpoints/CatalogBrandListEndpoint.cs",
"chars": 1359,
"preview": "using System.Linq;\nusing System.Threading.Tasks;\nusing AutoMapper;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft."
},
{
"path": "src/PublicApi/CatalogItemEndpoints/CatalogItemDto.cs",
"chars": 377,
"preview": "namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;\n\npublic class CatalogItemDto\n{\n public int Id { get; se"
},
{
"path": "src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemRequest.cs",
"chars": 272,
"preview": "namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;\n\npublic class GetByIdCatalogItemRequest : BaseRequest\n{\n "
},
{
"path": "src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.GetByIdCatalogItemResponse.cs",
"chars": 335,
"preview": "using System;\n\nnamespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;\n\npublic class GetByIdCatalogItemResponse : "
},
{
"path": "src/PublicApi/CatalogItemEndpoints/CatalogItemGetByIdEndpoint.cs",
"chars": 1865,
"preview": "using System.Threading.Tasks;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspN"
},
{
"path": "src/PublicApi/CatalogItemEndpoints/CatalogItemListPagedEndpoint.ListPagedCatalogItemRequest.cs",
"chars": 566,
"preview": "namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;\n\npublic class ListPagedCatalogItemRequest : BaseRequest\n{\n"
}
]
// ... and 224 more files (download for full content)
About this extraction
This page contains the full source code of the MicrosoftLearning/eShopOnWeb GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 424 files (611.6 KB), approximately 152.2k tokens, and a symbol index with 723 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.