Showing preview only (562K chars total). Download the full file or copy to clipboard to get everything.
Repository: jasontaylordev/CleanArchitecture
Branch: main
Commit: 40f7fe69c9f0
Files: 311
Total size: 488.1 KB
Directory structure:
gitextract_y908z1c3/
├── .aspire/
│ └── settings.json
├── .azdo/
│ └── pipelines/
│ └── azure-dev.yml
├── .devcontainer/
│ ├── README.md
│ └── devcontainer.json
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ └── workflows/
│ ├── azure-dev.yml
│ ├── build.yml
│ ├── codeql.yml
│ ├── release.yml
│ └── test-templates.yml
├── .gitignore
├── .template.config/
│ ├── dotnetcli.host.json
│ ├── ide.host.json
│ └── template.json
├── CODE_OF_CONDUCT.md
├── CleanArchitecture.nuspec
├── CleanArchitecture.slnx
├── Directory.Build.props
├── Directory.Packages.props
├── LICENSE
├── README-template.md
├── README.md
├── azure.yaml
├── build/
│ ├── build.ps1
│ ├── repack.ps1
│ └── test.ps1
├── global.json
├── infra/
│ ├── README.md
│ ├── abbreviations.json
│ ├── core/
│ │ ├── ai/
│ │ │ ├── cognitiveservices.bicep
│ │ │ ├── hub-dependencies.bicep
│ │ │ ├── hub.bicep
│ │ │ └── project.bicep
│ │ ├── config/
│ │ │ └── configstore.bicep
│ │ ├── database/
│ │ │ ├── cosmos/
│ │ │ │ ├── cosmos-account.bicep
│ │ │ │ ├── mongo/
│ │ │ │ │ ├── cosmos-mongo-account.bicep
│ │ │ │ │ └── cosmos-mongo-db.bicep
│ │ │ │ └── sql/
│ │ │ │ ├── cosmos-sql-account.bicep
│ │ │ │ ├── cosmos-sql-db.bicep
│ │ │ │ ├── cosmos-sql-role-assign.bicep
│ │ │ │ └── cosmos-sql-role-def.bicep
│ │ │ ├── mysql/
│ │ │ │ └── flexibleserver.bicep
│ │ │ ├── postgresql/
│ │ │ │ └── flexibleserver.bicep
│ │ │ └── sqlserver/
│ │ │ └── sqlserver.bicep
│ │ ├── gateway/
│ │ │ └── apim.bicep
│ │ ├── host/
│ │ │ ├── ai-environment.bicep
│ │ │ ├── aks-agent-pool.bicep
│ │ │ ├── aks-managed-cluster.bicep
│ │ │ ├── aks.bicep
│ │ │ ├── appservice-appsettings.bicep
│ │ │ ├── appservice.bicep
│ │ │ ├── appserviceplan.bicep
│ │ │ ├── container-app-upsert.bicep
│ │ │ ├── container-app.bicep
│ │ │ ├── container-apps-environment.bicep
│ │ │ ├── container-apps.bicep
│ │ │ ├── container-registry.bicep
│ │ │ ├── functions.bicep
│ │ │ └── staticwebapp.bicep
│ │ ├── monitor/
│ │ │ ├── applicationinsights-dashboard.bicep
│ │ │ ├── applicationinsights.bicep
│ │ │ ├── loganalytics.bicep
│ │ │ └── monitoring.bicep
│ │ ├── networking/
│ │ │ ├── cdn-endpoint.bicep
│ │ │ ├── cdn-profile.bicep
│ │ │ └── cdn.bicep
│ │ ├── search/
│ │ │ └── search-services.bicep
│ │ ├── security/
│ │ │ ├── aks-managed-cluster-access.bicep
│ │ │ ├── configstore-access.bicep
│ │ │ ├── keyvault-access.bicep
│ │ │ ├── keyvault-secret.bicep
│ │ │ ├── keyvault.bicep
│ │ │ ├── registry-access.bicep
│ │ │ └── role.bicep
│ │ ├── storage/
│ │ │ └── storage-account.bicep
│ │ └── testing/
│ │ └── loadtesting.bicep
│ ├── main.bicep
│ ├── main.parameters.json
│ └── services/
│ └── web.bicep
├── renovate.json
├── src/
│ ├── AppHost/
│ │ ├── AppHost.csproj
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── launchSettings.json
│ │ ├── appsettings.Development.json
│ │ └── appsettings.json
│ ├── Application/
│ │ ├── Application.csproj
│ │ ├── Common/
│ │ │ ├── Behaviours/
│ │ │ │ ├── AuthorizationBehaviour.cs
│ │ │ │ ├── LoggingBehaviour.cs
│ │ │ │ ├── PerformanceBehaviour.cs
│ │ │ │ ├── UnhandledExceptionBehaviour.cs
│ │ │ │ └── ValidationBehaviour.cs
│ │ │ ├── Exceptions/
│ │ │ │ ├── ForbiddenAccessException.cs
│ │ │ │ └── ValidationException.cs
│ │ │ ├── Interfaces/
│ │ │ │ ├── IApplicationDbContext.cs
│ │ │ │ ├── IIdentityService.cs
│ │ │ │ └── IUser.cs
│ │ │ ├── Mappings/
│ │ │ │ └── MappingExtensions.cs
│ │ │ ├── Models/
│ │ │ │ ├── LookupDto.cs
│ │ │ │ ├── PaginatedList.cs
│ │ │ │ └── Result.cs
│ │ │ └── Security/
│ │ │ └── AuthorizeAttribute.cs
│ │ ├── DependencyInjection.cs
│ │ ├── GlobalUsings.cs
│ │ ├── TodoItems/
│ │ │ ├── Commands/
│ │ │ │ ├── CreateTodoItem/
│ │ │ │ │ ├── CreateTodoItem.cs
│ │ │ │ │ └── CreateTodoItemCommandValidator.cs
│ │ │ │ ├── DeleteTodoItem/
│ │ │ │ │ └── DeleteTodoItem.cs
│ │ │ │ ├── UpdateTodoItem/
│ │ │ │ │ ├── UpdateTodoItem.cs
│ │ │ │ │ └── UpdateTodoItemCommandValidator.cs
│ │ │ │ └── UpdateTodoItemDetail/
│ │ │ │ └── UpdateTodoItemDetail.cs
│ │ │ ├── EventHandlers/
│ │ │ │ ├── LogTodoItemCompleted.cs
│ │ │ │ └── LogTodoItemCreated.cs
│ │ │ └── Queries/
│ │ │ └── GetTodoItemsWithPagination/
│ │ │ ├── GetTodoItemsWithPagination.cs
│ │ │ ├── GetTodoItemsWithPaginationQueryValidator.cs
│ │ │ └── TodoItemBriefDto.cs
│ │ ├── TodoLists/
│ │ │ ├── Commands/
│ │ │ │ ├── CreateTodoList/
│ │ │ │ │ ├── CreateTodoList.cs
│ │ │ │ │ └── CreateTodoListCommandValidator.cs
│ │ │ │ ├── DeleteTodoList/
│ │ │ │ │ └── DeleteTodoList.cs
│ │ │ │ ├── PurgeTodoLists/
│ │ │ │ │ └── PurgeTodoLists.cs
│ │ │ │ └── UpdateTodoList/
│ │ │ │ ├── UpdateTodoList.cs
│ │ │ │ └── UpdateTodoListCommandValidator.cs
│ │ │ └── Queries/
│ │ │ └── GetTodos/
│ │ │ ├── GetTodos.cs
│ │ │ ├── TodoItemDto.cs
│ │ │ ├── TodoListDto.cs
│ │ │ └── TodosVm.cs
│ │ └── WeatherForecasts/
│ │ └── Queries/
│ │ └── GetWeatherForecasts/
│ │ ├── GetWeatherForecastsQuery.cs
│ │ └── WeatherForecast.cs
│ ├── Domain/
│ │ ├── Common/
│ │ │ ├── BaseAuditableEntity.cs
│ │ │ ├── BaseEntity.cs
│ │ │ ├── BaseEvent.cs
│ │ │ └── ValueObject.cs
│ │ ├── Constants/
│ │ │ ├── Policies.cs
│ │ │ └── Roles.cs
│ │ ├── Domain.csproj
│ │ ├── Entities/
│ │ │ ├── TodoItem.cs
│ │ │ └── TodoList.cs
│ │ ├── Enums/
│ │ │ └── PriorityLevel.cs
│ │ ├── Events/
│ │ │ ├── TodoItemCompletedEvent.cs
│ │ │ ├── TodoItemCreatedEvent.cs
│ │ │ └── TodoItemDeletedEvent.cs
│ │ ├── Exceptions/
│ │ │ └── UnsupportedColourException.cs
│ │ ├── GlobalUsings.cs
│ │ └── ValueObjects/
│ │ └── Colour.cs
│ ├── Infrastructure/
│ │ ├── Data/
│ │ │ ├── ApplicationDbContext.cs
│ │ │ ├── ApplicationDbContextInitialiser.cs
│ │ │ ├── Configurations/
│ │ │ │ ├── TodoItemConfiguration.cs
│ │ │ │ └── TodoListConfiguration.cs
│ │ │ └── Interceptors/
│ │ │ ├── AuditableEntityInterceptor.cs
│ │ │ └── DispatchDomainEventsInterceptor.cs
│ │ ├── DependencyInjection.cs
│ │ ├── GlobalUsings.cs
│ │ ├── Identity/
│ │ │ ├── ApplicationUser.cs
│ │ │ ├── IdentityResultExtensions.cs
│ │ │ └── IdentityService.cs
│ │ └── Infrastructure.csproj
│ ├── ServiceDefaults/
│ │ ├── Extensions.cs
│ │ └── ServiceDefaults.csproj
│ ├── Shared/
│ │ ├── Services.cs
│ │ └── Shared.csproj
│ └── Web/
│ ├── ClientApp/
│ │ ├── .editorconfig
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── angular.json
│ │ ├── default.conf.template
│ │ ├── karma.conf.js
│ │ ├── nswag.json
│ │ ├── package.json
│ │ ├── proxy.conf.js
│ │ ├── src/
│ │ │ ├── api-authorization/
│ │ │ │ ├── auth.guard.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── authorize.interceptor.spec.ts
│ │ │ │ ├── authorize.interceptor.ts
│ │ │ │ ├── login/
│ │ │ │ │ ├── login.component.html
│ │ │ │ │ └── login.component.ts
│ │ │ │ └── register/
│ │ │ │ ├── register.component.html
│ │ │ │ └── register.component.ts
│ │ │ ├── app/
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── app.server.module.ts
│ │ │ │ ├── counter/
│ │ │ │ │ ├── counter.component.html
│ │ │ │ │ ├── counter.component.spec.ts
│ │ │ │ │ └── counter.component.ts
│ │ │ │ ├── fetch-data/
│ │ │ │ │ ├── fetch-data.component.html
│ │ │ │ │ └── fetch-data.component.ts
│ │ │ │ ├── home/
│ │ │ │ │ ├── home.component.html
│ │ │ │ │ └── home.component.ts
│ │ │ │ ├── nav-menu/
│ │ │ │ │ ├── nav-menu.component.html
│ │ │ │ │ ├── nav-menu.component.scss
│ │ │ │ │ └── nav-menu.component.ts
│ │ │ │ ├── theme-toggle/
│ │ │ │ │ ├── theme-toggle.component.html
│ │ │ │ │ └── theme-toggle.component.ts
│ │ │ │ ├── theme.service.ts
│ │ │ │ ├── todo/
│ │ │ │ │ ├── todo.component.html
│ │ │ │ │ ├── todo.component.scss
│ │ │ │ │ └── todo.component.ts
│ │ │ │ └── weather/
│ │ │ │ ├── weather.component.html
│ │ │ │ └── weather.component.ts
│ │ │ ├── assets/
│ │ │ │ └── .gitkeep
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── styles.scss
│ │ │ └── test.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
│ ├── ClientApp-React/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── aspnetcore-https.cjs
│ │ ├── index.html
│ │ ├── nswag.json
│ │ ├── package.json
│ │ ├── public/
│ │ │ └── manifest.webmanifest
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── AppRoutes.jsx
│ │ │ ├── components/
│ │ │ │ ├── Counter.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ ├── Layout.jsx
│ │ │ │ ├── NavMenu.jsx
│ │ │ │ ├── ThemeContext.jsx
│ │ │ │ ├── ThemeToggle.jsx
│ │ │ │ ├── Todo.jsx
│ │ │ │ ├── Weather.jsx
│ │ │ │ └── api-authorization/
│ │ │ │ ├── AuthContext.jsx
│ │ │ │ ├── LoginPage.jsx
│ │ │ │ ├── ProtectedRoute.jsx
│ │ │ │ └── RegisterPage.jsx
│ │ │ ├── main.jsx
│ │ │ ├── styles.scss
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── DependencyInjection.cs
│ ├── Endpoints/
│ │ ├── TodoItems.cs
│ │ ├── TodoLists.cs
│ │ ├── Users.cs
│ │ └── WeatherForecasts.cs
│ ├── GlobalUsings.cs
│ ├── Infrastructure/
│ │ ├── ApiExceptionOperationTransformer.cs
│ │ ├── BearerSecuritySchemeTransformer.cs
│ │ ├── EndpointRouteBuilderExtensions.cs
│ │ ├── IEndpointGroup.cs
│ │ ├── IdentityApiOperationTransformer.cs
│ │ ├── MethodInfoExtensions.cs
│ │ ├── ProblemDetailsExceptionHandler.cs
│ │ └── WebApplicationExtensions.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Services/
│ │ └── CurrentUser.cs
│ ├── Web-webapi.http
│ ├── Web.csproj
│ ├── Web.http
│ ├── appsettings.PostgreSQL.json
│ ├── appsettings.SQLServer.json
│ ├── appsettings.SQLite.json
│ └── appsettings.json
├── templates/
│ └── ca-use-case/
│ ├── .template.config/
│ │ ├── dotnetcli.host.json
│ │ └── template.json
│ └── FeatureName/
│ ├── Commands/
│ │ └── CleanArchitectureUseCase/
│ │ └── CleanArchitectureUseCase.cs
│ └── Queries/
│ └── CleanArchitectureUseCase/
│ └── CleanArchitectureUseCase.cs
└── tests/
├── Application.FunctionalTests/
│ ├── Application.FunctionalTests.csproj
│ ├── FunctionalTestSetup.cs
│ ├── GlobalUsings.cs
│ ├── Infrastructure/
│ │ ├── DatabaseResetter.cs
│ │ ├── TestApp.cs
│ │ ├── TestBase.cs
│ │ └── WebApiFactory.cs
│ ├── TodoItems/
│ │ └── Commands/
│ │ ├── CreateTodoItemTests.cs
│ │ ├── DeleteTodoItemTests.cs
│ │ ├── UpdateTodoItemDetailTests.cs
│ │ └── UpdateTodoItemTests.cs
│ └── TodoLists/
│ ├── Commands/
│ │ ├── CreateTodoListTests.cs
│ │ ├── DeleteTodoListTests.cs
│ │ ├── PurgeTodoListsTests.cs
│ │ └── UpdateTodoListTests.cs
│ └── Queries/
│ └── GetTodosTests.cs
├── Application.UnitTests/
│ ├── Application.UnitTests.csproj
│ └── Common/
│ ├── Behaviours/
│ │ └── RequestLoggerTests.cs
│ ├── Exceptions/
│ │ └── ValidationExceptionTests.cs
│ ├── Mappings/
│ │ └── MappingTests.cs
│ └── Models/
│ └── PaginatedListTests.cs
├── Domain.UnitTests/
│ ├── Domain.UnitTests.csproj
│ └── ValueObjects/
│ └── ColourTests.cs
├── Infrastructure.IntegrationTests/
│ ├── GlobalUsings.cs
│ └── Infrastructure.IntegrationTests.csproj
├── TestAppHost/
│ ├── Program.cs
│ └── TestAppHost.csproj
└── Web.AcceptanceTests/
├── AspireSetup.cs
├── Features/
│ ├── Counter.feature
│ ├── Home.feature
│ ├── Login.feature
│ └── Weather.feature
├── GlobalUsings.cs
├── Pages/
│ ├── BasePage.cs
│ ├── CounterPage.cs
│ ├── HomePage.cs
│ ├── LoginPage.cs
│ └── WeatherPage.cs
├── PlaywrightSetup.cs
├── StepDefinitions/
│ ├── CounterStepDefinitions.cs
│ ├── HomeStepDefinitions.cs
│ ├── LoginStepDefinitions.cs
│ └── WeatherStepDefinitions.cs
└── Web.AcceptanceTests.csproj
================================================
FILE CONTENTS
================================================
================================================
FILE: .aspire/settings.json
================================================
{
"appHostPath": "../src/AppHost/AppHost.csproj"
}
================================================
FILE: .azdo/pipelines/azure-dev.yml
================================================
# Run when commits are pushed to mainline branch (main or master)
# Set this to the mainline branch you are using
trigger:
- main
- master
# Azure Pipelines workflow to deploy to Azure using azd
# To configure required secrets and service connection for connecting to Azure, simply run `azd pipeline config --provider azdo`
# Task "Install azd" needs to install setup-azd extension for azdo - https://marketplace.visualstudio.com/items?itemName=ms-azuretools.azd
# See below for alternative task to install azd if you can't install above task in your organization
pool:
vmImage: ubuntu-latest
steps:
# setup-azd@1 needs to be manually installed in your organization
# if you can't install it, you can use the below bash script to install azd
# and remove this step
- task: setup-azd@1
displayName: Install azd
# If you can't install above task in your organization, you can comment it and uncomment below task to install azd
# - task: Bash@3
# displayName: Install azd
# inputs:
# targetType: 'inline'
# script: |
# curl -fsSL https://aka.ms/install-azd.sh | bash
- task: UseDotNet@2
displayName: Setup .NET
inputs:
useGlobalJson: true
# azd delegate auth to az to use service connection with AzureCLI@2
- pwsh: |
azd config set auth.useAzCliAuth "true"
displayName: Configure AZD to Use AZ CLI Authentication.
- task: AzureCLI@2
displayName: Provision Infrastructure
inputs:
azureSubscription: azconnection
scriptType: bash
scriptLocation: inlineScript
keepAzSessionActive: true
inlineScript: |
azd provision --no-prompt
env:
AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID)
AZURE_ENV_NAME: $(AZURE_ENV_NAME)
AZURE_LOCATION: $(AZURE_LOCATION)
AZD_INITIAL_ENVIRONMENT_CONFIG: $(secrets.AZD_INITIAL_ENVIRONMENT_CONFIG)
- task: AzureCLI@2
displayName: Deploy Application
inputs:
azureSubscription: azconnection
scriptType: bash
scriptLocation: inlineScript
keepAzSessionActive: true
inlineScript: |
azd deploy --no-prompt
env:
AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID)
AZURE_ENV_NAME: $(AZURE_ENV_NAME)
AZURE_LOCATION: $(AZURE_LOCATION)
================================================
FILE: .devcontainer/README.md
================================================
# Dev Container
This folder contains configuration for running the project inside a **Dev Container** (VS Code Remote Containers or GitHub Codespaces).
## What this folder does
- Defines the development environment (SDK versions, tools, extensions)
- Ensures every developer uses the same environment
- Simplifies onboarding by eliminating local machine setup issues
- Supports GitHub Codespaces for cloud-based development
## When to use it
If using VS Code:
1. Install the “Dev Containers” extension.
2. Open the repository.
3. Select **“Reopen in Container”**.
If using GitHub Codespaces:
- Codespaces will automatically use this configuration when the workspace starts.
## Notes
This folder has no effect on the runtime application. It is only used to configure the developer environment.
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "Azure Developer CLI",
"image": "mcr.microsoft.com/devcontainers/python:3.13-bullseye",
"features": {
// See https://containers.dev/features for list of features
"ghcr.io/devcontainers/features/docker-in-docker:2": {
},
"ghcr.io/azure/azure-dev/azd:latest": {}
},
"customizations": {
"vscode": {
"extensions": [
"GitHub.vscode-github-actions",
"ms-azuretools.azure-dev",
"ms-azuretools.vscode-azurefunctions",
"ms-azuretools.vscode-bicep",
"ms-azuretools.vscode-docker"
// Include other VSCode language extensions if needed
// Right click on an extension inside VSCode to add directly to devcontainer.json, or copy the extension ID
]
}
},
"forwardPorts": [
// Forward ports if needed for local development
],
"postCreateCommand": "",
"remoteUser": "vscode",
"hostRequirements": {
"memory": "8gb"
}
}
================================================
FILE: .editorconfig
================================================
root = true
# All files
[*]
indent_style = space
# Xml files
[*.{xml,csproj,props,targets,ruleset,nuspec,resx}]
indent_size = 2
# Javascript files
[*.js]
indent_size = 2
# Json files
[*.{json,config}]
indent_size = 2
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
tab_width = 4
# New line preferences
end_of_line = lf
insert_final_newline = true
#### .NET Coding Conventions ####
[*.{cs,vb}]
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
file_header_template = unset
# this. and Me. preferences
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = 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_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Expression-level preferences
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Field preferences
dotnet_style_readonly_field = true:warning
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = none
#### C# Coding Conventions ####
[*.cs]
# var preferences
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:suggestion
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
# Code-block preferences
csharp_prefer_braces = true:silent
csharp_prefer_simple_using_statement = true:suggestion
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
csharp_style_namespace_declarations = file_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
#### Naming styles ####
[*.{cs,vb}]
# Naming rules
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.events_should_be_pascalcase.symbols = events
dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
# Symbol specifications
dotnet_naming_symbols.interfaces.applicable_kinds = interface
dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interfaces.required_modifiers =
dotnet_naming_symbols.enums.applicable_kinds = enum
dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enums.required_modifiers =
dotnet_naming_symbols.events.applicable_kinds = event
dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.events.required_modifiers =
dotnet_naming_symbols.methods.applicable_kinds = method
dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.methods.required_modifiers =
dotnet_naming_symbols.properties.applicable_kinds = property
dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.properties.required_modifiers =
dotnet_naming_symbols.public_fields.applicable_kinds = field
dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_fields.required_modifiers =
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_fields.required_modifiers =
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_static_fields.required_modifiers = static
dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types_and_namespaces.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.type_parameters.applicable_kinds = type_parameter
dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
dotnet_naming_symbols.type_parameters.required_modifiers =
dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_constant_fields.required_modifiers = const
dotnet_naming_symbols.local_variables.applicable_kinds = local
dotnet_naming_symbols.local_variables.applicable_accessibilities = local
dotnet_naming_symbols.local_variables.required_modifiers =
dotnet_naming_symbols.local_constants.applicable_kinds = local
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
dotnet_naming_symbols.local_constants.required_modifiers = const
dotnet_naming_symbols.parameters.applicable_kinds = parameter
dotnet_naming_symbols.parameters.applicable_accessibilities = *
dotnet_naming_symbols.parameters.required_modifiers =
dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_constant_fields.required_modifiers = const
dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_symbols.local_functions.applicable_accessibilities = *
dotnet_naming_symbols.local_functions.required_modifiers =
# Naming styles
dotnet_naming_style.pascalcase.required_prefix =
dotnet_naming_style.pascalcase.required_suffix =
dotnet_naming_style.pascalcase.word_separator =
dotnet_naming_style.pascalcase.capitalization = pascal_case
dotnet_naming_style.ipascalcase.required_prefix = I
dotnet_naming_style.ipascalcase.required_suffix =
dotnet_naming_style.ipascalcase.word_separator =
dotnet_naming_style.ipascalcase.capitalization = pascal_case
dotnet_naming_style.tpascalcase.required_prefix = T
dotnet_naming_style.tpascalcase.required_suffix =
dotnet_naming_style.tpascalcase.word_separator =
dotnet_naming_style.tpascalcase.capitalization = pascal_case
dotnet_naming_style._camelcase.required_prefix = _
dotnet_naming_style._camelcase.required_suffix =
dotnet_naming_style._camelcase.word_separator =
dotnet_naming_style._camelcase.capitalization = camel_case
dotnet_naming_style.camelcase.required_prefix =
dotnet_naming_style.camelcase.required_suffix =
dotnet_naming_style.camelcase.word_separator =
dotnet_naming_style.camelcase.capitalization = camel_case
dotnet_naming_style.s_camelcase.required_prefix = s_
dotnet_naming_style.s_camelcase.required_suffix =
dotnet_naming_style.s_camelcase.word_separator =
dotnet_naming_style.s_camelcase.capitalization = camel_case
dotnet_style_namespace_match_folder = true:suggestion
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: JasonTaylorDev # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report 🐛
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: Ask a question ❓
url: https://github.com/jasontaylordev/cleanarchitecture/discussions/new
about: Ask a question or request support for using the template
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request ✨
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/azure-dev.yml
================================================
name: Azure deployment
on:
workflow_dispatch:
push:
# Run when commits are pushed to mainline branch (main or master)
# Set this to the mainline branch you are using
branches:
- main
- master
# GitHub Actions workflow to deploy to Azure using azd
# To configure required secrets for connecting to Azure, simply run `azd pipeline config`
# Set up permissions for deploying with secretless Azure federated credentials
# https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication
permissions:
id-token: write
contents: read
jobs:
build:
runs-on: ubuntu-latest
env:
AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install azd
uses: Azure/setup-azd@v2
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
global-json-file: 'global.json'
- name: Log in with Azure (Federated Credentials)
run: |
azd auth login `
--client-id "$Env:AZURE_CLIENT_ID" `
--federated-credential-provider "github" `
--tenant-id "$Env:AZURE_TENANT_ID"
shell: pwsh
- name: Provision Infrastructure
run: azd provision --no-prompt
env:
AZD_INITIAL_ENVIRONMENT_CONFIG: ${{ secrets.AZD_INITIAL_ENVIRONMENT_CONFIG }}
- name: Deploy Application
run: azd deploy --no-prompt
================================================
FILE: .github/workflows/build.yml
================================================
name: Build
on:
pull_request:
branches: [ main ]
paths-ignore:
- '.scripts/**'
- .gitignore
- CODE_OF_CONDUCT.md
- LICENSE
- README.md
workflow_call:
inputs:
build-artifacts:
type: boolean
required: true
default: false
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
name: Checkout code
- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
#if (!UseApiOnly)
- name: Install Node & cache npm packages
uses: actions/setup-node@v6
with:
node-version: '24.x'
cache: 'npm'
cache-dependency-path: |
src/Web/ClientApp/package-lock.json
src/Web/ClientApp-React/package-lock.json
#endif
- name: Install .NET
uses: actions/setup-dotnet@v5
- name: Restore solution
run: dotnet restore
- name: Build solution
run: dotnet build --no-restore --configuration Release
#if (!UseApiOnly)
- name: Install Playwright browsers
run: pwsh artifacts/bin/Web.AcceptanceTests/release/playwright.ps1 install --with-deps chromium
#endif
- name: Test solution
run: dotnet test --no-build --configuration Release
================================================
FILE: .github/workflows/codeql.yml
================================================
name: CodeQL
on:
push:
branches: [ main ]
paths-ignore:
- .gitignore
- CODE_OF_CONDUCT.md
- LICENSE
- README.md
pull_request:
branches: [ main ]
schedule:
- cron: '00 0 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
- name: Autobuild
uses: github/codeql-action/autobuild@v4
env:
SkipNSwag: True
npm_config_legacy_peer_deps: true
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
release:
types: [published]
permissions:
contents: read
jobs:
publish:
name: Publish to NuGet.org
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
name: Checkout
- uses: nuget/setup-nuget@v2
name: Set up NuGet
with:
nuget-version: 'latest'
- name: Install Mono
run: sudo apt-get update && sudo apt-get install -y mono-complete
- name: Extract version
id: version
run: echo "value=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
- name: Update template version
run: |
jq --arg v "${{ steps.version.outputs.value }}" \
'.symbols.caPackageVersion.parameters.value = $v' \
.template.config/template.json > tmp.json && mv tmp.json .template.config/template.json
- name: Update nuspec release notes
env:
RELEASE_NOTES: ${{ github.event.release.body }}
run: |
python3 - <<'EOF'
import os, xml.etree.ElementTree as ET
ET.register_namespace('', 'http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd')
tree = ET.parse('CleanArchitecture.nuspec')
root = tree.getroot()
ns = {'n': 'http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd'}
root.find('.//n:releaseNotes', ns).text = os.environ.get('RELEASE_NOTES', '')
tree.write('CleanArchitecture.nuspec', xml_declaration=True, encoding='utf-8')
EOF
- name: Pack
run: nuget pack CleanArchitecture.nuspec -NoDefaultExcludes -Version ${{ steps.version.outputs.value }}
- name: Upload package artifact
uses: actions/upload-artifact@v7
with:
name: nuget-package
path: '*.nupkg'
- name: Publish
run: nuget push *.nupkg -Source 'https://api.nuget.org/v3/index.json' -ApiKey ${{secrets.NUGET_API_KEY}} -SkipDuplicate
================================================
FILE: .github/workflows/test-templates.yml
================================================
name: Test Templates
on:
push:
branches: [ main ]
workflow_dispatch:
permissions:
contents: read
jobs:
test-template:
name: ${{ matrix.client-framework }}-${{ matrix.database }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
client-framework: [angular, react, none]
database: [sqlite, sqlserver, postgresql]
steps:
- uses: actions/checkout@v6
name: Checkout code
- name: Install .NET
uses: actions/setup-dotnet@v5
- name: Install Node & cache npm packages
if: matrix.client-framework != 'none'
uses: actions/setup-node@v6
with:
node-version: '24.x'
cache: 'npm'
cache-dependency-path: |
src/Web/ClientApp/package-lock.json
src/Web/ClientApp-React/package-lock.json
- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ matrix.client-framework }}-${{ matrix.database }}-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Install template
run: dotnet new install .
- name: Generate solution
run: |
dotnet new ca-sln \
--client-framework ${{ matrix.client-framework }} \
--database ${{ matrix.database }} \
--name CleanArchitecture \
--output generated \
--no-update-check
- name: Build solution
working-directory: generated
run: dotnet build --configuration Release
- name: Build client app
if: matrix.client-framework != 'none'
working-directory: generated/src/Web/ClientApp
run: |
npm ci
npm run build
- name: Install Playwright browsers
if: matrix.client-framework != 'none'
working-directory: generated
run: pwsh artifacts/bin/Web.AcceptanceTests/release/playwright.ps1 install --with-deps chromium
- name: Test solution
working-directory: generated
run: dotnet test --no-build --configuration Release
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from `dotnet new gitignore`
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
tools/
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Auto-generated OpenAPI specification and TypeScript API clients
src/Web/wwwroot/openapi/v1.json
src/Web/ClientApp/src/app/web-api-client.ts
src/Web/ClientApp-React/src/web-api-client.ts
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.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
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# 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
# Note: 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
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable 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
*.appx
*.appxbundle
*.appxupload
# 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
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# 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
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# 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/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# Vim temporary swap files
*.swp
.azure
# SQLite database files
*.db
*.db-shm
*.db-wal
================================================
FILE: .template.config/dotnetcli.host.json
================================================
{
"symbolInfo": {
"ClientFramework": {
"longName": "client-framework",
"shortName": "cf"
},
"PipelineProvider": {
"longName": "pipeline-provider",
"shortName": "pp"
},
"Database": {
"longName": "database",
"shortName": "db"
}
}
}
================================================
FILE: .template.config/ide.host.json
================================================
{
"$schema": "http://json.schemastore.org/vs-2017.3.host",
"order": 0,
"icon": "icon.png",
"symbolInfo": [
{
"id": "ClientFramework",
"name": {
"text": "Client Framework"
},
"description": {
"text": "Select the Client Framework type, or select None for Web API only."
},
"isVisible": true
},
{
"id": "PipelineProvider",
"name": {
"text": "Pipeline Provider"
},
"description": {
"text": "Select the pipeline provider."
},
"isVisible": true
},
{
"id": "Database",
"name": {
"text": "Database"
},
"description": {
"text": "Select the database type."
},
"isVisible": true
},
]
}
================================================
FILE: .template.config/template.json
================================================
{
"$schema": "http://json.schemastore.org/template",
"author": "JasonTaylorDev",
"classifications": [
"Angular",
"API",
"Aspire",
"Clean Architecture",
"Cloud",
"NUnit",
"Playwright",
"React",
"Test",
"Web",
"Web API"],
"name": "Clean Architecture Solution",
"description": "A Clean Architecture Solution Template supporting Angular, React, and Web API-only apps built with ASP.NET Core and Aspire.",
"identity": "Clean.Architecture.Solution.CSharp",
"groupIdentity": "Clean.Architecture.Solution",
"shortName": "ca-sln",
"tags": {
"language": "C#",
"type": "project"
},
"sourceName": "CleanArchitecture",
"preferNameDirectory": true,
"symbols": {
"kestrelHttpPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the HTTP endpoint in launchSettings.json."
},
"kestrelHttpPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 5000,
"high": 5300
}
},
"kestrelHttpPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "kestrelHttpPort",
"fallbackVariableName": "kestrelHttpPortGenerated"
},
"replaces": "5000"
},
"kestrelHttpsPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the HTTPS endpoint in launchSettings.json."
},
"kestrelHttpsPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 7000,
"high": 7300
}
},
"kestrelHttpsPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "kestrelHttpsPort",
"fallbackVariableName": "kestrelHttpsPortGenerated"
},
"replaces": "5001"
},
"appHostHttpPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the HTTP endpoint in launchSettings.json of the AppHost project."
},
"appHostHttpPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 15000,
"high": 15300
}
},
"appHostHttpPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "appHostHttpPort",
"fallbackVariableName": "appHostHttpPortGenerated"
},
"replaces": "15000"
},
"appHostHttpsPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the HTTPS endpoint in launchSettings.json of the AppHost project."
},
"appHostHttpsPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 17000,
"high": 17300
}
},
"appHostHttpsPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "appHostHttpsPort",
"fallbackVariableName": "appHostHttpsPortGenerated"
},
"replaces": "17000"
},
"appHostOtlpHttpPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the OTLP HTTP endpoint in launchSettings.json of the AppHost project."
},
"appHostOtlpHttpPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 19000,
"high": 19300
}
},
"appHostOtlpHttpPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "appHostOtlpHttpPort",
"fallbackVariableName": "appHostOtlpHttpPortGenerated"
},
"replaces": "19000"
},
"appHostOtlpHttpsPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the OTLP HTTPS endpoint in launchSettings.json of the AppHost project."
},
"appHostOtlpHttpsPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 21000,
"high": 21300
}
},
"appHostOtlpHttpsPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "appHostOtlpHttpsPort",
"fallbackVariableName": "appHostOtlpHttpsPortGenerated"
},
"replaces": "21000"
},
"appHostResourceHttpPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the resource service HTTP endpoint in launchSettings.json of the AppHost project."
},
"appHostResourceHttpPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 20000,
"high": 20300
}
},
"appHostResourceHttpPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "appHostResourceHttpPort",
"fallbackVariableName": "appHostResourceHttpPortGenerated"
},
"replaces": "20000"
},
"appHostResourceHttpsPort": {
"type": "parameter",
"datatype": "integer",
"description": "Port number to use for the resource service HTTPS endpoint in launchSettings.json of the AppHost project."
},
"appHostResourceHttpsPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 22000,
"high": 22300
}
},
"appHostResourceHttpsPortReplacer": {
"type": "generated",
"generator": "coalesce",
"parameters": {
"sourceVariableName": "appHostResourceHttpsPort",
"fallbackVariableName": "appHostResourceHttpsPortGenerated"
},
"replaces": "22000"
},
"caPackageVersion": {
"type": "generated",
"generator": "constant",
"replaces": "caPackageVersion",
"parameters": {
"value": "0.0.0"
}
},
"caRepositoryUrl": {
"type": "generated",
"generator": "constant",
"replaces": "caRepositoryUrl",
"parameters": {
"value": "https://github.com/jasontaylordev/CleanArchitecture"
}
},
"caDocsUrl": {
"type": "generated",
"generator": "constant",
"replaces": "caDocsUrl",
"parameters": {
"value": "https://cleanarchitecture.jasontaylor.dev"
}
},
"ClientFramework": {
"type": "parameter",
"datatype": "choice",
"choices": [
{
"choice": "Angular",
"description": "Use Angular"
},
{
"choice": "React",
"description": "Use React"
},
{
"choice": "None",
"description": "Web API only"
}
],
"defaultValue": "Angular",
"description": "The type of client framework to use"
},
"UseAngular": {
"type": "computed",
"value": "(ClientFramework == \"Angular\")"
},
"UseReact": {
"type": "computed",
"value": "(ClientFramework == \"React\")"
},
"UseApiOnly": {
"type": "computed",
"value": "(ClientFramework == \"None\")"
},
"Database": {
"type": "parameter",
"datatype": "choice",
"choices": [
{
"choice": "postgresql",
"description": "PostgreSQL"
},
{
"choice": "sqlite",
"description": "SQLite"
},
{
"choice": "sqlserver",
"description": "SQL Server"
}
],
"defaultValue": "sqlite",
"description": "The database type to use."
},
"PipelineProvider": {
"type": "parameter",
"datatype": "choice",
"choices": [
{
"choice": "azdo",
"description": "Azure Pipelines"
},
{
"choice": "github",
"description": "GitHub Actions"
}
],
"defaultValue": "github",
"description": "The pipeline provider to use (github for Github Actions and azdo for Azure Pipelines)."
},
"UseAzurePipelines": {
"type": "computed",
"value": "(PipelineProvider == \"azdo\")"
},
"UseGithubActions": {
"type": "computed",
"value": "(PipelineProvider == \"github\")"
},
"UsePostgreSQL": {
"type": "computed",
"value": "(Database == \"postgresql\")"
},
"UseSqlite": {
"type": "computed",
"value": "(Database == \"sqlite\")"
},
"UseSqlServer": {
"type": "computed",
"value": "(Database == \"sqlserver\")"
}
},
"sources": [
{
"source": "./",
"target": "./",
"exclude": [
".azure/**/*",
".template.config/**/*",
"templates/**/*",
"**/*.filelist",
"**/*.user",
"**/*.lock.json",
"*.nuspec",
"src/Web/appsettings.json",
"src/Web/appsettings.PostgreSQL.json",
"src/Web/appsettings.SQLite.json",
"src/Web/appsettings.SQLServer.json",
"tests/Application.FunctionalTests/PostgreSQLTestcontainersTestDatabase.cs",
"tests/Application.FunctionalTests/PostgreSQLTestDatabase.cs",
"tests/Application.FunctionalTests/SqliteTestDatabase.cs",
"tests/Application.FunctionalTests/SqlTestcontainersTestDatabase.cs",
"tests/Application.FunctionalTests/SqlTestDatabase.cs",
"tests/Application.FunctionalTests/appsettings.json",
"tests/Application.FunctionalTests/appsettings.PostgreSQL.json",
"tests/Application.FunctionalTests/appsettings.SQLServer.json",
".azdo/**/*",
".github/**/*"
],
"rename": {
"README-template.md": "README.md"
},
"modifiers": [
{
"condition": "(!UseApiOnly)",
"exclude": [
"src/Web/Infrastructure/BearerSecuritySchemeTransformer.cs"
]
},
{
"condition": "(UseAngular)",
"exclude": [
"src/Web/ClientApp-React/**",
"src/Web/Web-webapi.http"
]
},
{
"condition": "(UseReact)",
"exclude": [
"src/Web/ClientApp/**",
"src/Web/Web-webapi.http"
],
"rename": {
"ClientApp-React": "ClientApp"
}
},
{
"condition": "(UseApiOnly)",
"exclude": [
"src/Web/ClientApp/**",
"src/Web/ClientApp-React/**",
"src/Web/Pages/**",
"src/Web/Web.http",
"tests/Web.AcceptanceTests/**"
],
"rename": {
"Web-webapi.http": "Web.http"
}
},
{
"condition": "(UseAzurePipelines)",
"include": [
".azdo/**/*"
]
},
{
"condition": "(UseGithubActions)",
"include": [
".github/**/*"
]
},
{
"condition": "(UsePostgreSQL)",
"include": [
"src/Web/appsettings.PostgreSQL.json",
"tests/Application.FunctionalTests/PostgreSQLTestcontainersTestDatabase.cs",
"tests/Application.FunctionalTests/PostgreSQLTestDatabase.cs",
"tests/Application.FunctionalTests/appsettings.PostgreSQL.json"
],
"rename": {
"src/Web/appsettings.PostgreSQL.json": "src/Web/appsettings.json",
"tests/Application.FunctionalTests/appsettings.PostgreSQL.json": "tests/Application.FunctionalTests/appsettings.json"
}
},
{
"condition": "(UseSqlServer)",
"include": [
"src/Web/appsettings.SQLServer.json",
"tests/Application.FunctionalTests/SqlTestcontainersTestDatabase.cs",
"tests/Application.FunctionalTests/SqlTestDatabase.cs",
"tests/Application.FunctionalTests/appsettings.SQLServer.json"
],
"rename": {
"src/Web/appsettings.SQLServer.json": "src/Web/appsettings.json",
"tests/Application.FunctionalTests/appsettings.SQLServer.json": "tests/Application.FunctionalTests/appsettings.json"
}
},
{
"condition": "(UseSqlite)",
"include": [
"src/Web/appsettings.SQLite.json",
"tests/Application.FunctionalTests/SqliteTestDatabase.cs"
],
"rename": {
"src/Web/appsettings.SQLite.json": "src/Web/appsettings.json"
}
}
]
}
]
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behaviour that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behaviour by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behaviour and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behaviour.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviours that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behaviour may be
reported by contacting the project team at hello@jasontaylor.dev. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CleanArchitecture.nuspec
================================================
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Clean.Architecture.Solution.Template</id>
<version>0.0.0</version>
<title>Clean Architecture Solution Template</title>
<authors>JasonTaylorDev</authors>
<description>A Clean Architecture Solution Template supporting Angular, React, and Web API-only apps built with ASP.NET Core and Aspire.</description>
<summary>
A Clean Architecture Solution Template supporting Angular, React, and Web API-only apps built with ASP.NET Core and Aspire.
</summary>
<releaseNotes></releaseNotes>
<projectUrl>https://cleanarchitecture.jasontaylor.dev</projectUrl>
<repository type="git" url="https://github.com/JasonTaylorDev/CleanArchitecture.git" branch="main" />
<license type="expression">MIT</license>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<tags>clean-architecture project template csharp dotnet angular react webapi aspire</tags>
<icon>icon.png</icon>
<readme>README.md</readme>
<packageTypes>
<packageType name="Template" />
</packageTypes>
</metadata>
<files>
<file src=".template.config\icon.png" />
<file src="README.md" />
<file src=".\**" target="content" exclude="**\node_modules\**;**\tools\**;**\bin\**;**\obj\**;.\.vs\**;.\.vscode\**;**\ClientApp\dist\**;**\ClientApp\.angular\**;**\wwwroot\dist\**;content\Directory.Build.*;.\.git\**;.\.github\workflows\jekyll-gh-pages.yml;.\.github\workflows\release.yml;.\.github\workflows\codeql.yml;.\.github\workflows\build.yml;.\.github\ISSUE_TEMPLATE\**;.\.github\icon.png;.\.github\FUNDING.yml;.\CODE_OF_CONDUCT.md;.\LICENSE;.\README.md;.\CleanArchitecture.nuspec;.\src\Web\app.db;.\renovate.json;.\build\**;.\.github\workflows\test-templates.yml;.\artifacts\**;" />
</files>
</package>
================================================
FILE: CleanArchitecture.slnx
================================================
<Solution>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
<File Path=".gitignore" />
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
<File Path="global.json" />
<File Path="README.md" />
</Folder>
<Folder Name="/src/">
<Project Path="src/AppHost/AppHost.csproj" DefaultStartup="true" />
<Project Path="src/ServiceDefaults/ServiceDefaults.csproj" />
<Project Path="src/Application/Application.csproj" />
<Project Path="src/Domain/Domain.csproj" />
<Project Path="src/Infrastructure/Infrastructure.csproj" />
<Project Path="src/Shared/Shared.csproj" />
<Project Path="src/Web/Web.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Application.FunctionalTests/Application.FunctionalTests.csproj" />
<Project Path="tests/TestAppHost/TestAppHost.csproj" />
<Project Path="tests/Application.UnitTests/Application.UnitTests.csproj" />
<Project Path="tests/Domain.UnitTests/Domain.UnitTests.csproj" />
<Project Path="tests/Infrastructure.IntegrationTests/Infrastructure.IntegrationTests.csproj" />
<!--#if (!UseApiOnly)-->
<Project Path="tests/Web.AcceptanceTests/Web.AcceptanceTests.csproj" />
<!--#endif-->
</Folder>
</Solution>
================================================
FILE: Directory.Build.props
================================================
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!--#if (UsePostgreSQL)-->
<!-- Suppress NU1608 until Npgsql.EntityFrameworkCore.PostgreSQL 10.0.0 is released -->
<WarningsNotAsErrors>NU1608</WarningsNotAsErrors>
<!--#endif-->
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
================================================
FILE: Directory.Packages.props
================================================
<!-- For more info on central package management go to https://devblogs.microsoft.com/nuget/introducing-central-package-management/ -->
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.GuardClauses" Version="5.0.0" />
<PackageVersion Include="AutoMapper" Version="16.1.1" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.5.0" />
<PackageVersion Include="Azure.Identity" Version="1.19.0" />
<PackageVersion Include="coverlet.collector" Version="8.0.1" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
<PackageVersion Include="MediatR" Version="14.1.0" />
<PackageVersion Include="MediatR.Contracts" Version="2.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.5" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.5" />
<PackageVersion Include="Microsoft.Build.Tasks.Core" Version="18.4.0" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="18.4.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.5" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="nunit" Version="4.5.1" />
<PackageVersion Include="NUnit.Analyzers" Version="4.12.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageVersion Include="Respawn" Version="7.0.0" />
<PackageVersion Include="Scalar.AspNetCore" Version="2.13.11" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.16.0" />
<!--#if(!UseApiOnly)-->
<PackageVersion Include="Microsoft.Playwright" Version="1.58.0" />
<PackageVersion Include="Reqnroll.NUnit" Version="3.3.3" />
<!--#endif-->
<!--#if (UsePostgreSQL)-->
<PackageVersion Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.PostgreSQL" Version="13.1.3" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
<!--#endif-->
<!--#if (UseSqlServer)-->
<PackageVersion Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.SqlServer" Version="13.1.3" />
<!--#endif-->
<!--#if (UseSqlite)-->
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
<PackageVersion Include="CommunityToolkit.Aspire.Hosting.SQLite" Version="13.1.1" />
<!--#endif-->
<PackageVersion Include="Aspire.Hosting.AppHost" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.Testing" Version="13.1.3" />
<PackageVersion Include="Aspire.Hosting.JavaScript" Version="13.1.3" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.4.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.4.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.0" />
</ItemGroup>
</Project>
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 JasonTaylorDev
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: README-template.md
================================================
# CleanArchitecture
The project was generated using the [Clean.Architecture.Solution.Template](caRepositoryUrl) version caPackageVersion.
## Build
Run `dotnet build` to build the solution.
## Run
To run the application:
```bash
dotnet run --project .\src\AppHost
```
The Aspire dashboard will open automatically, showing the application URLs and logs.
## Code Styles & Formatting
The template includes [EditorConfig](https://editorconfig.org/) support to help maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs. The **.editorconfig** file defines the coding styles applicable to this solution.
## Code Scaffolding
The template includes support to scaffold new commands and queries.
Start in the `.\src\Application\` folder.
Create a new command:
```
dotnet new ca-usecase --name CreateTodoList --feature-name TodoLists --usecase-type command --return-type int
```
Create a new query:
```
dotnet new ca-usecase -n GetTodos -fn TodoLists -ut query -rt TodosVm
```
If you encounter the error *"No templates or subcommands found matching: 'ca-usecase'."*, install the template and try again:
```bash
dotnet new install Clean.Architecture.Solution.Template::caPackageVersion
```
## Test
The solution contains unit, integration, and functional tests.
To run the tests:
```bash
dotnet test
```
## Help
To learn more about the template go to the [project website](caDocsUrl). Here you can find additional guidance, request new features, report a bug, and discuss the template with other users.
================================================
FILE: README.md
================================================
# Clean Architecture Solution Template
[](https://github.com/jasontaylordev/CleanArchitecture/actions/workflows/build.yml)
[](https://github.com/jasontaylordev/CleanArchitecture/actions/workflows/codeql.yml)
[](https://www.nuget.org/packages/Clean.Architecture.Solution.Template)
[](https://www.nuget.org/packages/Clean.Architecture.Solution.Template)

The goal of this template is to provide a straightforward and efficient approach to enterprise application development, leveraging the power of Clean Architecture and ASP.NET Core. Using this template, you can effortlessly create a new app with Angular, React, or Web API only, powered by ASP.NET Core and Aspire. Getting started is easy - simply install the **.NET template** (see below for full details).
For full documentation, visit **[cleanarchitecture.jasontaylor.dev](https://cleanarchitecture.jasontaylor.dev)**.
If you find this project useful, please give it a star. Thanks! ⭐
## Getting Started
The following prerequisites are required to build and run the solution:
- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) (latest version)
- [Node.js](https://nodejs.org/) (latest LTS, only required if you are using Angular or React)
The easiest way to get started is to install the [.NET template](https://www.nuget.org/packages/Clean.Architecture.Solution.Template):
```
dotnet new install Clean.Architecture.Solution.Template
```
Once installed, create a new solution using the template. You can choose to use Angular, React, or create a Web API-only solution. Specify the client framework using the `-cf` or `--client-framework` option, and provide the output directory where your project will be created. Here are some examples:
To create a Single-Page Application (SPA) with Angular and ASP.NET Core:
```bash
dotnet new ca-sln --client-framework Angular --output YourProjectName
```
To create a SPA with React and ASP.NET Core:
```bash
dotnet new ca-sln -cf React -o YourProjectName
```
To create a ASP.NET Core Web API-only solution:
```bash
dotnet new ca-sln -cf None -o YourProjectName
```
Launch the app:
```bash
cd src/AppHost
dotnet run
```
To learn more, run the following command:
```bash
dotnet new ca-sln --help
```
You can create use cases (commands or queries) by navigating to `./src/Application` and running `dotnet new ca-usecase`. Here are some examples:
To create a new command:
```bash
dotnet new ca-usecase --name CreateTodoList --feature-name TodoLists --usecase-type command --return-type int
```
To create a query:
```bash
dotnet new ca-usecase -n GetTodos -fn TodoLists -ut query -rt TodosVm
```
To learn more, run the following command:
```bash
dotnet new ca-usecase --help
```
## Database
The template supports [PostgreSQL](https://www.postgresql.org), [SQLite](https://www.sqlite.org/) (default), and [SQL Server](https://learn.microsoft.com/en-us/sql/sql-server/what-is-sql-server). Specify the database to use with the `--database` option:
```bash
dotnet new ca-sln --database [postgresql|sqlite|sqlserver]
```
On application startup, the database is automatically **deleted**, **recreated**, and **seeded** using `ApplicationDbContextInitialiser`. This is a practical strategy for early development, avoiding the overhead of maintaining migrations while keeping the schema and sample data in sync with the domain model.
This process includes:
- Deleting the existing database
- Recreating the schema from the current model
- Seeding default roles, users, and data
For production environments, consider using EF Core migrations or migration bundles during deployment.
For more information, see [Database Initialisation Strategies for EF Core](https://jasontaylor.dev/ef-core-database-initialisation-strategies).
## Deploy
This template is structured to follow the Azure Developer CLI (azd). You can learn more about `azd` in the [official documentation](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli). To get started:
```bash
# Log in to Azure
azd auth login
# Provision and deploy to Azure
azd up
```
To set up a CI/CD pipeline (GitHub Actions or Azure DevOps):
```bash
azd pipeline config
```
## API Documentation
This template includes built-in API documentation using [ASP.NET Core OpenAPI](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/overview) and [Scalar](https://scalar.com/). Once the application is running, navigate to `/scalar` to explore the API using the Scalar UI.
The OpenAPI specification is generated at build time and written to `wwwroot/openapi/v1.json`.
## Technologies
* [ASP.NET Core 10](https://docs.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core)
* [Aspire](https://aspire.dev)
* [Entity Framework Core 10](https://docs.microsoft.com/en-us/ef/core/)
* [Angular 21](https://angular.dev/) or [React 19](https://react.dev/)
* [MediatR](https://github.com/jbogard/MediatR)
* [AutoMapper](https://automapper.org/)
* [FluentValidation](https://fluentvalidation.net/)
* [NUnit](https://nunit.org/), [Shouldly](https://docs.shouldly.org/), [Moq](https://github.com/devlooped/moq) & [Respawn](https://github.com/jbogard/Respawn)
* [Scalar](https://scalar.com/)
## Versions
The main branch is now on .NET 10.0. The following previous versions are available:
* [9.0](https://github.com/jasontaylordev/CleanArchitecture/tree/net9.0)
* [8.0](https://github.com/jasontaylordev/CleanArchitecture/tree/net8.0)
* [7.0](https://github.com/jasontaylordev/CleanArchitecture/tree/net7.0)
* [6.0](https://github.com/jasontaylordev/CleanArchitecture/tree/net6.0)
* [5.0](https://github.com/jasontaylordev/CleanArchitecture/tree/net5.0)
* [3.1](https://github.com/jasontaylordev/CleanArchitecture/tree/netcore3.1)
## Learn More
* [Clean Architecture Solution Template Documentation](https://cleanarchitecture.jasontaylor.dev)
* [Clean Architecture with ASP.NET Core 3.0 (GOTO 2019)](https://youtu.be/dK4Yb6-LxAk)
## Support
If you are having problems, please let me know by [raising a new issue](https://github.com/jasontaylordev/CleanArchitecture/issues/new/choose).
## License
This project is licensed with the [MIT license](LICENSE).
================================================
FILE: azure.yaml
================================================
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
# Name of the application.
name: clean-architecture-azd
services:
web:
language: csharp
project: ./src/Web
host: appservice
================================================
FILE: build/build.ps1
================================================
param (
[string]$Target = "Default",
[string]$Configuration = "Release"
)
$ErrorActionPreference = "Stop"
$solution = "CleanArchitecture.slnx"
$clientApps = @("./src/Web/ClientApp", "./src/Web/ClientApp-React")
Write-Host "Building solution..."
dotnet build $solution --configuration $Configuration
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
foreach ($clientApp in $clientApps) {
if (Test-Path $clientApp) {
Write-Host "Installing client dependencies ($clientApp)..."
Push-Location $clientApp
try {
npm ci
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
npm run build
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
} finally {
Pop-Location
}
}
}
Write-Host "Testing solution..."
if ($Target -eq "Basic") {
$testProjects = Get-ChildItem -Path "tests" -Filter "*.csproj" -Recurse |
Where-Object { $_.Name -notlike "*AcceptanceTests*" }
foreach ($project in $testProjects) {
dotnet test $project.FullName --no-build --configuration $Configuration
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
} else {
dotnet test $solution --no-build --configuration $Configuration
exit $LASTEXITCODE
}
================================================
FILE: build/repack.ps1
================================================
$ErrorActionPreference = "Stop"
$root = Split-Path $PSScriptRoot -Parent
$name = "Clean.Architecture.Solution.Template.0.0.0.nupkg"
$pkg = Join-Path $root "artifacts\$name"
Set-Location $root
dotnet new uninstall Clean.Architecture.Solution.Template 2>$null
nuget pack "CleanArchitecture.nuspec" -NoDefaultExcludes -OutputDirectory "artifacts"
dotnet new install $pkg --force
Remove-Item $pkg -Force 2>$null
================================================
FILE: build/test.ps1
================================================
param (
[string[]]$ClientFramework = @("angular", "react", "none"),
[string[]]$Database = @("sqlite", "sqlserver", "postgresql")
)
$outputPath = Join-Path (Split-Path $PSScriptRoot -Parent) "artifacts\template-tests"
$results = @()
function CreateAndTestProject {
param (
[string]$clientFramework,
[string]$database
)
$name = "$clientFramework-$database"
$projectPath = Join-Path $outputPath $name
try {
if (Test-Path $projectPath) {
Write-Host "Removing existing directory: $name"
Remove-Item -Recurse -Force $projectPath
}
Write-Host "Creating project: $name"
$startTime = Get-Date
dotnet new ca-sln --client-framework $clientFramework --database $database --name CleanArchitecture --output $projectPath --no-update-check
if ($LASTEXITCODE -ne 0) { throw "dotnet new ca-sln failed for $name" }
$exitCode = 0
Push-Location $projectPath
try {
Write-Host "Building: $name"
dotnet build --configuration Release
if ($LASTEXITCODE -ne 0) { throw "Build failed for $name" }
if ($clientFramework -ne "none") {
Write-Host "Building client app: $name"
Push-Location "./src/Web/ClientApp"
try {
npm ci
if ($LASTEXITCODE -ne 0) { throw "npm ci failed for $name" }
npm run build
if ($LASTEXITCODE -ne 0) { throw "npm build failed for $name" }
} finally {
Pop-Location
}
}
if ($clientFramework -ne "none") {
Write-Host "Installing Playwright browsers: $name"
pwsh artifacts/bin/Web.AcceptanceTests/release/playwright.ps1 install --with-deps chromium
if ($LASTEXITCODE -ne 0) { throw "Playwright install failed for $name" }
}
Write-Host "Testing: $name"
dotnet test --no-build --configuration Release
if ($LASTEXITCODE -ne 0) { $exitCode = $LASTEXITCODE }
} finally {
Pop-Location
}
$endTime = Get-Date
$duration = $endTime - $startTime
$script:results += [PSCustomObject]@{
ClientFramework = $clientFramework
Database = $database
ExitCode = $exitCode
Status = if ($exitCode -eq 0) { "Success" } else { "Failure" }
Duration = $duration.ToString("c")
}
} catch {
Write-Host "An error occurred while processing: $name"
Write-Host $_.Exception.Message
$script:results += [PSCustomObject]@{
ClientFramework = $clientFramework
Database = $database
ExitCode = -1
Status = "Error"
Duration = "00:00:00.0000000"
}
}
}
if (-not (Test-Path $outputPath)) {
New-Item -ItemType Directory -Path $outputPath | Out-Null
}
foreach ($cf in $ClientFramework) {
foreach ($db in $Database) {
CreateAndTestProject -clientFramework $cf -database $db
}
}
$results | Format-Table -Property ClientFramework, Database, Status, Duration -AutoSize
if ($results | Where-Object { $_.Status -ne "Success" }) {
exit 1
}
================================================
FILE: global.json
================================================
{
"sdk": {
"version": "10.0.201",
"rollForward": "latestFeature"
}
}
================================================
FILE: infra/README.md
================================================
# Infrastructure (infra)
This folder contains infrastructure-as-code and deployment templates used with the **Azure Developer CLI (azd)**.
## What this folder includes
- Bicep files for provisioning Azure resources
- Configuration for App Service, hosting, Key Vault, and databases
- Deployment workflows used by `azd up`
- Environment configuration for cloud deployment
## How to use it
### Prerequisites
- Azure account
- Azure Developer CLI installed (`azd`)
### Deploying with azd
```bash
azd auth login
azd up
```
================================================
FILE: infra/abbreviations.json
================================================
{
"analysisServicesServers": "as",
"apiManagementService": "apim-",
"appConfigurationStores": "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-",
"cognitiveServicesSpeech": "cog-sp-",
"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-",
"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",
"loadTesting": "lt-",
"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-",
"postgreSQLServers": "psql-",
"postgreSQLServersDatabases": "psqldb-",
"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/core/ai/cognitiveservices.bicep
================================================
metadata description = 'Creates an Azure Cognitive Services instance.'
param name string
param location string = resourceGroup().location
param tags object = {}
@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.')
param customSubDomainName string = name
param disableLocalAuth bool = false
param deployments array = []
param kind string = 'OpenAI'
@allowed([ 'Enabled', 'Disabled' ])
param publicNetworkAccess string = 'Enabled'
param sku object = {
name: 'S0'
}
param allowedIpRules array = []
param networkAcls object = empty(allowedIpRules) ? {
defaultAction: 'Allow'
} : {
ipRules: allowedIpRules
defaultAction: 'Deny'
}
resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = {
name: name
location: location
tags: tags
kind: kind
properties: {
customSubDomainName: customSubDomainName
publicNetworkAccess: publicNetworkAccess
networkAcls: networkAcls
disableLocalAuth: disableLocalAuth
}
sku: sku
}
@batchSize(1)
resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: {
parent: account
name: deployment.name
properties: {
model: deployment.model
raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null
}
sku: contains(deployment, 'sku') ? deployment.sku : {
name: 'Standard'
capacity: 20
}
}]
output endpoint string = account.properties.endpoint
output endpoints object = account.properties.endpoints
output id string = account.id
output name string = account.name
================================================
FILE: infra/core/ai/hub-dependencies.bicep
================================================
param location string = resourceGroup().location
param tags object = {}
@description('Name of the key vault')
param keyVaultName string
@description('Name of the storage account')
param storageAccountName string
@description('Name of the OpenAI cognitive services')
param openAiName string
@description('Array of OpenAI model deployments')
param openAiModelDeployments array = []
@description('Name of the Log Analytics workspace')
param logAnalyticsName string = ''
@description('Name of the Application Insights instance')
param applicationInsightsName string = ''
@description('Name of the container registry')
param containerRegistryName string = ''
@description('Name of the Azure Cognitive Search service')
param searchServiceName string = ''
module keyVault '../security/keyvault.bicep' = {
name: 'keyvault'
params: {
location: location
tags: tags
name: keyVaultName
}
}
module storageAccount '../storage/storage-account.bicep' = {
name: 'storageAccount'
params: {
location: location
tags: tags
name: storageAccountName
containers: [
{
name: 'default'
}
]
files: [
{
name: 'default'
}
]
queues: [
{
name: 'default'
}
]
tables: [
{
name: 'default'
}
]
corsRules: [
{
allowedOrigins: [
'https://mlworkspace.azure.ai'
'https://ml.azure.com'
'https://*.ml.azure.com'
'https://ai.azure.com'
'https://*.ai.azure.com'
'https://mlworkspacecanary.azure.ai'
'https://mlworkspace.azureml-test.net'
]
allowedMethods: [
'GET'
'HEAD'
'POST'
'PUT'
'DELETE'
'OPTIONS'
'PATCH'
]
maxAgeInSeconds: 1800
exposedHeaders: [
'*'
]
allowedHeaders: [
'*'
]
}
]
deleteRetentionPolicy: {
allowPermanentDelete: false
enabled: false
}
shareDeleteRetentionPolicy: {
enabled: true
days: 7
}
}
}
module logAnalytics '../monitor/loganalytics.bicep' =
if (!empty(logAnalyticsName)) {
name: 'logAnalytics'
params: {
location: location
tags: tags
name: logAnalyticsName
}
}
module applicationInsights '../monitor/applicationinsights.bicep' =
if (!empty(applicationInsightsName) && !empty(logAnalyticsName)) {
name: 'applicationInsights'
params: {
location: location
tags: tags
name: applicationInsightsName
logAnalyticsWorkspaceId: !empty(logAnalyticsName) ? logAnalytics.outputs.id : ''
}
}
module containerRegistry '../host/container-registry.bicep' =
if (!empty(containerRegistryName)) {
name: 'containerRegistry'
params: {
location: location
tags: tags
name: containerRegistryName
}
}
module cognitiveServices '../ai/cognitiveservices.bicep' = {
name: 'cognitiveServices'
params: {
location: location
tags: tags
name: openAiName
kind: 'AIServices'
deployments: openAiModelDeployments
}
}
module searchService '../search/search-services.bicep' =
if (!empty(searchServiceName)) {
name: 'searchService'
params: {
location: location
tags: tags
name: searchServiceName
}
}
output keyVaultId string = keyVault.outputs.id
output keyVaultName string = keyVault.outputs.name
output keyVaultEndpoint string = keyVault.outputs.endpoint
output storageAccountId string = storageAccount.outputs.id
output storageAccountName string = storageAccount.outputs.name
output containerRegistryId string = !empty(containerRegistryName) ? containerRegistry.outputs.id : ''
output containerRegistryName string = !empty(containerRegistryName) ? containerRegistry.outputs.name : ''
output containerRegistryEndpoint string = !empty(containerRegistryName) ? containerRegistry.outputs.loginServer : ''
output applicationInsightsId string = !empty(applicationInsightsName) ? applicationInsights.outputs.id : ''
output applicationInsightsName string = !empty(applicationInsightsName) ? applicationInsights.outputs.name : ''
output logAnalyticsWorkspaceId string = !empty(logAnalyticsName) ? logAnalytics.outputs.id : ''
output logAnalyticsWorkspaceName string = !empty(logAnalyticsName) ? logAnalytics.outputs.name : ''
output openAiId string = cognitiveServices.outputs.id
output openAiName string = cognitiveServices.outputs.name
output openAiEndpoint string = cognitiveServices.outputs.endpoints['OpenAI Language Model Instance API']
output searchServiceId string = !empty(searchServiceName) ? searchService.outputs.id : ''
output searchServiceName string = !empty(searchServiceName) ? searchService.outputs.name : ''
output searchServiceEndpoint string = !empty(searchServiceName) ? searchService.outputs.endpoint : ''
================================================
FILE: infra/core/ai/hub.bicep
================================================
@description('The AI Studio Hub Resource name')
param name string
@description('The display name of the AI Studio Hub Resource')
param displayName string = name
@description('The storage account ID to use for the AI Studio Hub Resource')
param storageAccountId string
@description('The key vault ID to use for the AI Studio Hub Resource')
param keyVaultId string
@description('The application insights ID to use for the AI Studio Hub Resource')
param applicationInsightsId string = ''
@description('The container registry ID to use for the AI Studio Hub Resource')
param containerRegistryId string = ''
@description('The OpenAI Cognitive Services account name to use for the AI Studio Hub Resource')
param openAiName string
@description('The OpenAI Cognitive Services account connection name to use for the AI Studio Hub Resource')
param openAiConnectionName string
@description('The Azure Cognitive Search service name to use for the AI Studio Hub Resource')
param aiSearchName string = ''
@description('The Azure Cognitive Search service connection name to use for the AI Studio Hub Resource')
param aiSearchConnectionName string
@description('The SKU name to use for the AI Studio Hub Resource')
param skuName string = 'Basic'
@description('The SKU tier to use for the AI Studio Hub Resource')
@allowed(['Basic', 'Free', 'Premium', 'Standard'])
param skuTier string = 'Basic'
@description('The public network access setting to use for the AI Studio Hub Resource')
@allowed(['Enabled','Disabled'])
param publicNetworkAccess string = 'Enabled'
param location string = resourceGroup().location
param tags object = {}
resource hub 'Microsoft.MachineLearningServices/workspaces@2024-04-01' = {
name: name
location: location
tags: tags
sku: {
name: skuName
tier: skuTier
}
kind: 'Hub'
identity: {
type: 'SystemAssigned'
}
properties: {
friendlyName: displayName
storageAccount: storageAccountId
keyVault: keyVaultId
applicationInsights: !empty(applicationInsightsId) ? applicationInsightsId : null
containerRegistry: !empty(containerRegistryId) ? containerRegistryId : null
hbiWorkspace: false
managedNetwork: {
isolationMode: 'Disabled'
}
v1LegacyMode: false
publicNetworkAccess: publicNetworkAccess
}
resource contentSafetyDefaultEndpoint 'endpoints' = {
name: 'Azure.ContentSafety'
properties: {
name: 'Azure.ContentSafety'
endpointType: 'Azure.ContentSafety'
associatedResourceId: openAi.id
}
}
resource openAiConnection 'connections' = {
name: openAiConnectionName
properties: {
category: 'AzureOpenAI'
authType: 'ApiKey'
isSharedToAll: true
target: openAi.properties.endpoints['OpenAI Language Model Instance API']
metadata: {
ApiVersion: '2023-07-01-preview'
ApiType: 'azure'
ResourceId: openAi.id
}
credentials: {
key: openAi.listKeys().key1
}
}
}
resource searchConnection 'connections' =
if (!empty(aiSearchName)) {
name: aiSearchConnectionName
properties: {
category: 'CognitiveSearch'
authType: 'ApiKey'
isSharedToAll: true
target: 'https://${search.name}.search.windows.net/'
credentials: {
key: !empty(aiSearchName) ? search.listAdminKeys().primaryKey : ''
}
}
}
}
resource openAi 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = {
name: openAiName
}
resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing =
if (!empty(aiSearchName)) {
name: aiSearchName
}
output name string = hub.name
output id string = hub.id
output principalId string = hub.identity.principalId
================================================
FILE: infra/core/ai/project.bicep
================================================
@description('The AI Studio Hub Resource name')
param name string
@description('The display name of the AI Studio Hub Resource')
param displayName string = name
@description('The name of the AI Studio Hub Resource where this project should be created')
param hubName string
@description('The name of the key vault resource to grant access to the project')
param keyVaultName string
@description('The SKU name to use for the AI Studio Hub Resource')
param skuName string = 'Basic'
@description('The SKU tier to use for the AI Studio Hub Resource')
@allowed(['Basic', 'Free', 'Premium', 'Standard'])
param skuTier string = 'Basic'
@description('The public network access setting to use for the AI Studio Hub Resource')
@allowed(['Enabled','Disabled'])
param publicNetworkAccess string = 'Enabled'
param location string = resourceGroup().location
param tags object = {}
resource project 'Microsoft.MachineLearningServices/workspaces@2024-04-01' = {
name: name
location: location
tags: tags
sku: {
name: skuName
tier: skuTier
}
kind: 'Project'
identity: {
type: 'SystemAssigned'
}
properties: {
friendlyName: displayName
hbiWorkspace: false
v1LegacyMode: false
publicNetworkAccess: publicNetworkAccess
hubResourceId: hub.id
}
}
module keyVaultAccess '../security/keyvault-access.bicep' = {
name: 'keyvault-access'
params: {
keyVaultName: keyVaultName
principalId: project.identity.principalId
}
}
module mlServiceRoleDataScientist '../security/role.bicep' = {
name: 'ml-service-role-data-scientist'
params: {
principalId: project.identity.principalId
roleDefinitionId: 'f6c7c914-8db3-469d-8ca1-694a8f32e121'
principalType: 'ServicePrincipal'
}
}
module mlServiceRoleSecretsReader '../security/role.bicep' = {
name: 'ml-service-role-secrets-reader'
params: {
principalId: project.identity.principalId
roleDefinitionId: 'ea01e6af-a1c1-4350-9563-ad00f8c72ec5'
principalType: 'ServicePrincipal'
}
}
resource hub 'Microsoft.MachineLearningServices/workspaces@2024-04-01' existing = {
name: hubName
}
output id string = project.id
output name string = project.name
output principalId string = project.identity.principalId
================================================
FILE: infra/core/config/configstore.bicep
================================================
metadata description = 'Creates an Azure App Configuration store.'
@description('The name for the Azure App Configuration store')
param name string
@description('The Azure region/location for the Azure App Configuration store')
param location string = resourceGroup().location
@description('Custom tags to apply to the Azure App Configuration store')
param tags object = {}
@description('Specifies the names of the key-value resources. The name is a combination of key and label with $ as delimiter. The label is optional.')
param keyValueNames array = []
@description('Specifies the values of the key-value resources.')
param keyValueValues array = []
@description('The principal ID to grant access to the Azure App Configuration store')
param principalId string
resource configStore 'Microsoft.AppConfiguration/configurationStores@2023-03-01' = {
name: name
location: location
sku: {
name: 'standard'
}
tags: tags
}
resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/keyValues@2023-03-01' = [for (item, i) in keyValueNames: {
parent: configStore
name: item
properties: {
value: keyValueValues[i]
tags: tags
}
}]
module configStoreAccess '../security/configstore-access.bicep' = {
name: 'app-configuration-access'
params: {
configStoreName: name
principalId: principalId
}
dependsOn: [configStore]
}
output endpoint string = configStore.properties.endpoint
================================================
FILE: infra/core/database/cosmos/cosmos-account.bicep
================================================
metadata description = 'Creates an Azure Cosmos DB account.'
param name string
param location string = resourceGroup().location
param tags object = {}
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
param keyVaultName string
@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ])
param kind string
resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' = {
name: name
kind: kind
location: location
tags: tags
properties: {
consistencyPolicy: { defaultConsistencyLevel: 'Session' }
locations: [
{
locationName: location
failoverPriority: 0
isZoneRedundant: false
}
]
databaseAccountOfferType: 'Standard'
enableAutomaticFailover: false
enableMultipleWriteLocations: false
apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {}
capabilities: [ { name: 'EnableServerless' } ]
minimalTlsVersion: 'Tls12'
}
}
resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
parent: keyVault
name: connectionStringKey
properties: {
value: cosmos.listConnectionStrings().connectionStrings[0].connectionString
}
}
resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' existing = {
name: keyVaultName
}
output connectionStringKey string = connectionStringKey
output endpoint string = cosmos.properties.documentEndpoint
output id string = cosmos.id
output name string = cosmos.name
================================================
FILE: infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep
================================================
metadata description = 'Creates an Azure Cosmos DB for MongoDB account.'
param name string
param location string = resourceGroup().location
param tags object = {}
param keyVaultName string
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
module cosmos '../../cosmos/cosmos-account.bicep' = {
name: 'cosmos-account'
params: {
name: name
location: location
connectionStringKey: connectionStringKey
keyVaultName: keyVaultName
kind: 'MongoDB'
tags: tags
}
}
output connectionStringKey string = cosmos.outputs.connectionStringKey
output endpoint string = cosmos.outputs.endpoint
output id string = cosmos.outputs.id
================================================
FILE: infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep
================================================
metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.'
param accountName string
param databaseName string
param location string = resourceGroup().location
param tags object = {}
param collections array = []
param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING'
param keyVaultName string
module cosmos 'cosmos-mongo-account.bicep' = {
name: 'cosmos-mongo-account'
params: {
name: accountName
location: location
keyVaultName: keyVaultName
tags: tags
connectionStringKey: connectionStringKey
}
}
resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-11-15' = {
name: '${accountName}/${databaseName}'
tags: tags
properties: {
resource: { id: databaseName }
}
resource list 'collections' = [for collection in collections: {
name: collection.name
properties: {
resource: {
id: collection.id
shardKey: { _id: collection.shardKey }
indexes: [ { key: { keys: [ collection.indexKey ] } } ]
}
}
}]
dependsOn: [
cosmos
]
}
output connectionStringKey string = connectionStringKey
output databaseName string = databaseName
output endpoint string = cosmos.outputs.endpoint
================================================
FILE: infra/core/database/cosmos/sql/cosmos-sql-account.bicep
================================================
metadata description = 'Creates an Azure Cosmos DB for NoSQL account.'
param name string
param location string = resourceGroup().location
param tags object = {}
param keyVaultName string
module cosmos '../../cosmos/cosmos-account.bicep' = {
name: 'cosmos-account'
params: {
name: name
location: location
tags: tags
keyVaultName: keyVaultName
kind: 'GlobalDocumentDB'
}
}
output connectionStringKey string = cosmos.outputs.connectionStringKey
output endpoint string = cosmos.outputs.endpoint
output id string = cosmos.outputs.id
output name string = cosmos.outputs.name
================================================
FILE: infra/core/database/cosmos/sql/cosmos-sql-db.bicep
================================================
metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.'
param accountName string
param databaseName string
param location string = resourceGroup().location
param tags object = {}
param containers array = []
param keyVaultName string
param principalIds array = []
module cosmos 'cosmos-sql-account.bicep' = {
name: 'cosmos-sql-account'
params: {
name: accountName
location: location
tags: tags
keyVaultName: keyVaultName
}
}
resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-11-15' = {
name: '${accountName}/${databaseName}'
properties: {
resource: { id: databaseName }
}
resource list 'containers' = [for container in containers: {
name: container.name
properties: {
resource: {
id: container.id
partitionKey: { paths: [ container.partitionKey ] }
}
options: {}
}
}]
dependsOn: [
cosmos
]
}
module roleDefinition 'cosmos-sql-role-def.bicep' = {
name: 'cosmos-sql-role-definition'
params: {
accountName: accountName
}
dependsOn: [
cosmos
database
]
}
// We need batchSize(1) here because sql role assignments have to be done sequentially
@batchSize(1)
module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) {
name: 'cosmos-sql-user-role-${uniqueString(principalId)}'
params: {
accountName: accountName
roleDefinitionId: roleDefinition.outputs.id
principalId: principalId
}
dependsOn: [
cosmos
database
]
}]
output accountId string = cosmos.outputs.id
output accountName string = cosmos.outputs.name
output connectionStringKey string = cosmos.outputs.connectionStringKey
output databaseName string = databaseName
output endpoint string = cosmos.outputs.endpoint
output roleDefinitionId string = roleDefinition.outputs.id
================================================
FILE: infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep
================================================
metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.'
param accountName string
param roleDefinitionId string
param principalId string = ''
resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-11-15' = {
parent: cosmos
name: guid(roleDefinitionId, principalId, cosmos.id)
properties: {
principalId: principalId
roleDefinitionId: roleDefinitionId
scope: cosmos.id
}
}
resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-11-15' existing = {
name: accountName
}
================================================
FILE: infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep
================================================
metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.'
param accountName string
resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-11-15' = {
parent: cosmos
name: guid(cosmos.id, accountName, 'sql-role')
properties: {
assignableScopes: [
cosmos.id
]
permissions: [
{
dataActions: [
'Microsoft.DocumentDB/databaseAccounts/readMetadata'
'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*'
'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*'
]
notDataActions: []
}
]
roleName: 'Reader Writer'
type: 'CustomRole'
}
}
resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-11-15' existing = {
name: accountName
}
output id string = roleDefinition.id
================================================
FILE: infra/core/database/mysql/flexibleserver.bicep
================================================
metadata description = 'Creates an Azure Database for MySQL - Flexible Server.'
param name string
param location string = resourceGroup().location
param tags object = {}
param sku object
param storage object
param administratorLogin string
@secure()
param administratorLoginPassword string
param highAvailabilityMode string = 'Disabled'
param databaseNames array = []
param allowAzureIPsFirewall bool = false
param allowAllIPsFirewall bool = false
param allowedSingleIPs array = []
// MySQL version
param version string
resource mysqlServer 'Microsoft.DBforMySQL/flexibleServers@2023-06-30' = {
location: location
tags: tags
name: name
sku: sku
properties: {
version: version
administratorLogin: administratorLogin
administratorLoginPassword: administratorLoginPassword
storage: storage
highAvailability: {
mode: highAvailabilityMode
}
}
resource database 'databases' = [for name in databaseNames: {
name: name
}]
resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) {
name: 'allow-all-IPs'
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '255.255.255.255'
}
}
resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) {
name: 'allow-all-azure-internal-IPs'
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '0.0.0.0'
}
}
resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: {
name: 'allow-single-${replace(ip, '.', '')}'
properties: {
startIpAddress: ip
endIpAddress: ip
}
}]
}
output MYSQL_DOMAIN_NAME string = mysqlServer.properties.fullyQualifiedDomainName
================================================
FILE: infra/core/database/postgresql/flexibleserver.bicep
================================================
metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.'
param name string
param location string = resourceGroup().location
param tags object = {}
param sku object
param storage object
param appUserLogin string
@secure()
param appUserLoginPassword string
param administratorLogin string
@secure()
param administratorLoginPassword string
param databaseName string
param allowAzureIPsFirewall bool = false
param allowAllIPsFirewall bool = false
param allowedSingleIPs array = []
param keyVaultName string
param connectionStringKey string
// PostgreSQL version
param version string
param utcNowString string = utcNow('yyyyMMddHHmm')
// Latest official version 2022-12-01 does not have Bicep types available
resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = {
location: location
tags: tags
name: name
sku: sku
properties: {
version: version
administratorLogin: administratorLogin
administratorLoginPassword: administratorLoginPassword
storage: storage
highAvailability: {
mode: 'Disabled'
}
}
resource database 'databases' = {
name: databaseName
}
resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) {
name: 'allow-all-IPs'
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '255.255.255.255'
}
}
resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) {
name: 'allow-all-azure-internal-IPs'
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '0.0.0.0'
}
}
resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: {
name: 'allow-single-${replace(ip, '.', '')}'
properties: {
startIpAddress: ip
endIpAddress: ip
}
}]
}
resource psqlDeploymentScript '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'
forceUpdateTag: utcNowString
environmentVariables: [
{
name: 'APPUSERLOGIN'
value: appUserLogin
}
{
name: 'APPUSERPASSWORD'
secureValue: appUserLoginPassword
}
{
name: 'DBNAME'
value: databaseName
}
{
name: 'DBSERVER'
value: name
}
{
name: 'ADMINLOGIN'
value: administratorLogin
}
{
name: 'ADMINLOGINPASSWORD'
secureValue: administratorLoginPassword
}
]
scriptContent: '''
apk add postgresql-client
cat << EOF > create_user.sql
CREATE ROLE "$APPUSERLOGIN" WITH LOGIN PASSWORD '$APPUSERPASSWORD';
GRANT ALL PRIVILEGES ON DATABASE $DBNAME TO "$APPUSERLOGIN";
EOF
psql "host=$DBSERVER.postgres.database.azure.com user=$ADMINLOGIN dbname=$DBNAME port=5432 password=$ADMINLOGINPASSWORD sslmode=require" < create_user.sql
'''
}
dependsOn: [
postgresServer
]
}
resource administratorLoginPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
parent: keyVault
name: 'dbAdminPassword'
properties: {
value: administratorLoginPassword
}
}
resource appUserLoginPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
parent: keyVault
name: 'dbAppUserPassword'
properties: {
value: appUserLoginPassword
}
}
resource sqlAzureConnectionStringSecret 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
parent: keyVault
name: connectionStringKey
properties: {
value: '${connectionString}; Password=${appUserLoginPassword}'
}
}
resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' existing = {
name: keyVaultName
}
var connectionString = 'Host=${postgresServer.properties.fullyQualifiedDomainName};Port=5432;Database=${databaseName};Username=${appUserLogin}'
output connectionStringKey string = connectionStringKey
================================================
FILE: infra/core/database/sqlserver/sqlserver.bicep
================================================
metadata description = 'Creates an Azure SQL Server instance.'
param name string
param location string = resourceGroup().location
param tags object = {}
param logAnalyticsWorkspaceId string = ''
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
param utcNowString string = utcNow('yyyyMMddHHmm')
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 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 sqlServerAuditingSettings 'Microsoft.Sql/servers/auditingSettings@2023-08-01-preview' = {
parent: sqlServer
name: 'default'
properties: {
state: 'Enabled'
isAzureMonitorTargetEnabled: true
}
}
resource sqlDatabase 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
parent: sqlServer
name: databaseName
location: location
}
resource sqlDatabaseDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!(empty(logAnalyticsWorkspaceId))) {
scope: sqlDatabase
name: 'sqlDatabaseDiagnosticSettings'
properties: {
workspaceId: logAnalyticsWorkspaceId
logs: [
{
category: 'SQLInsights'
enabled: true
}
{
category: 'AutomaticTuning'
enabled: true
}
{
category: 'QueryStoreRuntimeStatistics'
enabled: true
}
{
category: 'QueryStoreWaitStatistics'
enabled: true
}
{
category: 'Errors'
enabled: true
}
{
category: 'DatabaseWaitStatistics'
enabled: true
}
{
category: 'Timeouts'
enabled: true
}
{
category: 'Blocks'
enabled: true
}
{
category: 'Deadlocks'
enabled: true
}
]
metrics: [
{
category: 'Basic'
enabled: true
}
{
category: 'InstanceAndAppAdvanced'
enabled: true
}
{
category: 'WorkloadManagement'
enabled: true
}
]
}
}
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'
forceUpdateTag: utcNowString
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 if exists ${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-11-01' = {
parent: keyVault
name: 'dbAdminPassword'
properties: {
value: sqlAdminPassword
}
}
resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
parent: keyVault
name: 'dbAppUserPassword'
properties: {
value: appUserPassword
}
}
resource sqlAzureConnectionStringSecret 'Microsoft.KeyVault/vaults/secrets@2022-11-01' = {
parent: keyVault
name: connectionStringKey
properties: {
value: '${connectionString}; Password=${appUserPassword}'
}
}
resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' existing = {
name: keyVaultName
}
var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlDatabase.name}; User=${appUser}'
output connectionStringKey string = connectionStringKey
output databaseName string = sqlDatabase.name
================================================
FILE: infra/core/gateway/apim.bicep
================================================
metadata description = 'Creates an Azure API Management instance.'
param name string
param location string = resourceGroup().location
param tags object = {}
@description('The email address of the owner of the service')
@minLength(1)
param publisherEmail string = 'noreply@microsoft.com'
@description('The name of the owner of the service')
@minLength(1)
param publisherName string = 'n/a'
@description('The pricing tier of this API Management service')
@allowed([
'Consumption'
'Developer'
'Standard'
'Premium'
])
param sku string = 'Consumption'
@description('The instance size of this API Management service.')
@allowed([ 0, 1, 2 ])
param skuCount int = 0
@description('Azure Application Insights Name')
param applicationInsightsName string
resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = {
name: name
location: location
tags: union(tags, { 'azd-service-name': name })
sku: {
name: sku
capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount)
}
properties: {
publisherEmail: publisherEmail
publisherName: publisherName
// Custom properties are not supported for Consumption SKU
customProperties: sku == 'Consumption' ? {} : {
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false'
'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false'
}
}
}
resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) {
name: 'app-insights-logger'
parent: apimService
properties: {
credentials: {
instrumentationKey: applicationInsights.properties.InstrumentationKey
}
description: 'Logger to Azure Application Insights'
isBuffered: false
loggerType: 'applicationInsights'
resourceId: applicationInsights.id
}
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) {
name: applicationInsightsName
}
output apimServiceName string = apimService.name
================================================
FILE: infra/core/host/ai-environment.bicep
================================================
@minLength(1)
@description('Primary location for all resources')
param location string
@description('The AI Hub resource name.')
param hubName string
@description('The AI Project resource name.')
param projectName string
@description('The Key Vault resource name.')
param keyVaultName string
@description('The Storage Account resource name.')
param storageAccountName string
@description('The Open AI resource name.')
param openAiName string
@description('The Open AI connection name.')
param openAiConnectionName string
@description('The Open AI model deployments.')
param openAiModelDeployments array = []
@description('The Log Analytics resource name.')
param logAnalyticsName string = ''
@description('The Application Insights resource name.')
param applicationInsightsName string = ''
@description('The Container Registry resource name.')
param containerRegistryName string = ''
@description('The Azure Search resource name.')
param searchServiceName string = ''
@description('The Azure Search connection name.')
param searchConnectionName string = ''
param tags object = {}
module hubDependencies '../ai/hub-dependencies.bicep' = {
name: 'hubDependencies'
params: {
location: location
tags: tags
keyVaultName: keyVaultName
storageAccountName: storageAccountName
containerRegistryName: containerRegistryName
applicationInsightsName: applicationInsightsName
logAnalyticsName: logAnalyticsName
openAiName: openAiName
openAiModelDeployments: openAiModelDeployments
searchServiceName: searchServiceName
}
}
module hub '../ai/hub.bicep' = {
name: 'hub'
params: {
location: location
tags: tags
name: hubName
displayName: hubName
keyVaultId: hubDependencies.outputs.keyVaultId
storageAccountId: hubDependencies.outputs.storageAccountId
containerRegistryId: hubDependencies.outputs.containerRegistryId
applicationInsightsId: hubDependencies.outputs.applicationInsightsId
openAiName: hubDependencies.outputs.openAiName
openAiConnectionName: openAiConnectionName
aiSearchName: hubDependencies.outputs.searchServiceName
aiSearchConnectionName: searchConnectionName
}
}
module project '../ai/project.bicep' = {
name: 'project'
params: {
location: location
tags: tags
name: projectName
displayName: projectName
hubName: hub.outputs.name
keyVaultName: hubDependencies.outputs.keyVaultName
}
}
// Outputs
// Resource Group
output resourceGroupName string = resourceGroup().name
// Hub
output hubName string = hub.outputs.name
output hubPrincipalId string = hub.outputs.principalId
// Project
output projectName string = project.outputs.name
output projectPrincipalId string = project.outputs.principalId
// Key Vault
output keyVaultName string = hubDependencies.outputs.keyVaultName
output keyVaultEndpoint string = hubDependencies.outputs.keyVaultEndpoint
// Application Insights
output applicationInsightsName string = hubDependencies.outputs.applicationInsightsName
output logAnalyticsWorkspaceName string = hubDependencies.outputs.logAnalyticsWorkspaceName
// Container Registry
output containerRegistryName string = hubDependencies.outputs.containerRegistryName
output containerRegistryEndpoint string = hubDependencies.outputs.containerRegistryEndpoint
// Storage Account
output storageAccountName string = hubDependencies.outputs.storageAccountName
// Open AI
output openAiName string = hubDependencies.outputs.openAiName
output openAiEndpoint string = hubDependencies.outputs.openAiEndpoint
// Search
output searchServiceName string = hubDependencies.outputs.searchServiceName
output searchServiceEndpoint string = hubDependencies.outputs.searchServiceEndpoint
================================================
FILE: infra/core/host/aks-agent-pool.bicep
================================================
metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.'
param clusterName string
@description('The agent pool name')
param name string
@description('The agent pool configuration')
param config object
resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-11-01' existing = {
name: clusterName
}
resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-11-01' = {
parent: aksCluster
name: name
properties: config
}
================================================
FILE: infra/core/host/aks-managed-cluster.bicep
================================================
metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.'
@description('The name for the AKS managed cluster')
param name string
@description('The name of the resource group for the managed resources of the AKS cluster')
param nodeResourceGroupName string = ''
@description('The Azure region/location for the AKS resources')
param location string = resourceGroup().location
@description('Custom tags to apply to the AKS resources')
param tags object = {}
@description('Kubernetes Version')
param kubernetesVersion string = '1.29'
@description('Whether RBAC is enabled for local accounts')
param enableRbac bool = true
// Add-ons
@description('Whether web app routing (preview) add-on is enabled')
param webAppRoutingAddon bool = true
// AAD Integration
@description('Enable Azure Active Directory integration')
param enableAad bool = false
@description('Enable RBAC using AAD')
param enableAzureRbac bool = false
@description('The Tenant ID associated to the Azure Active Directory')
param aadTenantId string = tenant().tenantId
@description('The load balancer SKU to use for ingress into the AKS cluster')
@allowed([ 'basic', 'standard' ])
param loadBalancerSku string = 'standard'
@description('Network plugin used for building the Kubernetes network.')
@allowed([ 'azure', 'kubenet', 'none' ])
param networkPlugin string = 'azure'
@description('Network policy used for building the Kubernetes network.')
@allowed([ 'azure', 'calico' ])
param networkPolicy string = 'azure'
@description('If set to true, getting static credentials will be disabled for this cluster.')
param disableLocalAccounts bool = false
@description('The managed cluster SKU.')
@allowed([ 'Free', 'Paid', 'Standard' ])
param sku string = 'Free'
@description('Configuration of AKS add-ons')
param addOns object = {}
@description('The log analytics workspace id used for logging & monitoring')
param workspaceId string = ''
@description('The node pool configuration for the System agent pool')
param systemPoolConfig object
@description('The DNS prefix to associate with the AKS cluster')
param dnsPrefix string = ''
resource aks 'Microsoft.ContainerService/managedClusters@2023-11-01' = {
name: name
location: location
tags: tags
identity: {
type: 'SystemAssigned'
}
sku: {
name: 'Base'
tier: sku
}
properties: {
nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}'
kubernetesVersion: kubernetesVersion
dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix
enableRBAC: enableRbac
aadProfile: enableAad ? {
managed: true
enableAzureRBAC: enableAzureRbac
tenantID: aadTenantId
} : null
agentPoolProfiles: [
systemPoolConfig
]
networkProfile: {
loadBalancerSku: loadBalancerSku
networkPlugin: networkPlugin
networkPolicy: networkPolicy
}
disableLocalAccounts: disableLocalAccounts && enableAad
addonProfiles: addOns
ingressProfile: {
webAppRouting: {
enabled: webAppRoutingAddon
}
}
}
}
var aksDiagCategories = [
'cluster-autoscaler'
'kube-controller-manager'
'kube-audit-admin'
'guard'
]
// TODO: Update diagnostics to be its own module
// Blocking issue: https://github.com/Azure/bicep/issues/622
// Unable to pass in a `resource` scope or unable to use string interpolation in resource types
resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) {
name: 'aks-diagnostics'
scope: aks
properties: {
workspaceId: workspaceId
logs: [for category in aksDiagCategories: {
category: category
enabled: true
}]
metrics: [
{
category: 'AllMetrics'
enabled: true
}
]
}
}
@description('The resource name of the AKS cluster')
output clusterName string = aks.name
@description('The AKS cluster identity')
output clusterIdentity object = {
clientId: aks.properties.identityProfile.kubeletidentity.clientId
objectId: aks.properties.identityProfile.kubeletidentity.objectId
resourceId: aks.properties.identityProfile.kubeletidentity.resourceId
}
================================================
FILE: infra/core/host/aks.bicep
================================================
metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.'
@description('The name for the AKS managed cluster')
param name string
@description('The name for the Azure container registry (ACR)')
param containerRegistryName string
@description('The name of the connected log analytics workspace')
param logAnalyticsName string = ''
@description('The name of the keyvault to grant access')
param keyVaultName string
@description('The Azure region/location for the AKS resources')
param location string = resourceGroup().location
@description('Custom tags to apply to the AKS resources')
param tags object = {}
@description('AKS add-ons configuration')
param addOns object = {
azurePolicy: {
enabled: true
config: {
version: 'v2'
}
}
keyVault: {
enabled: true
config: {
enableSecretRotation: 'true'
rotationPollInterval: '2m'
}
}
openServiceMesh: {
enabled: false
config: {}
}
omsAgent: {
enabled: true
config: {}
}
applicationGateway: {
enabled: false
config: {}
}
}
@description('The managed cluster SKU.')
@allowed([ 'Free', 'Paid', 'Standard' ])
param sku string = 'Free'
@description('The load balancer SKU to use for ingress into the AKS cluster')
@allowed([ 'basic', 'standard' ])
param loadBalancerSku string = 'standard'
@description('Network plugin used for building the Kubernetes network.')
@allowed([ 'azure', 'kubenet', 'none' ])
param networkPlugin string = 'azure'
@description('Network policy used for building the Kubernetes network.')
@allowed([ 'azure', 'calico' ])
param networkPolicy string = 'azure'
@description('The DNS prefix to associate with the AKS cluster')
param dnsPrefix string = ''
@description('The name of the resource group for the managed resources of the AKS cluster')
param nodeResourceGroupName string = ''
@allowed([
'CostOptimised'
'Standard'
'HighSpec'
'Custom'
])
@description('The System Pool Preset sizing')
param systemPoolType string = 'CostOptimised'
@allowed([
''
'CostOptimised'
'Standard'
'HighSpec'
'Custom'
])
@description('The User Pool Preset sizing')
param agentPoolType string = ''
// Configure system / user agent pools
@description('Custom configuration of system node pool')
param systemPoolConfig object = {}
@description('Custom configuration of user node pool')
param agentPoolConfig object = {}
@description('Id of the user or app to assign application roles')
param principalId string = ''
@description('The type of principal to assign application roles')
@allowed(['Device','ForeignGroup','Group','ServicePrincipal','User'])
param principalType string = 'User'
@description('Kubernetes Version')
param kubernetesVersion string = '1.29'
@description('The Tenant ID associated to the Azure Active Directory')
param aadTenantId string = tenant().tenantId
@description('Whether RBAC is enabled for local accounts')
param enableRbac bool = true
@description('If set to true, getting static credentials will be disabled for this cluster.')
param disableLocalAccounts bool = false
@description('Enable RBAC using AAD')
param enableAzureRbac bool = false
// Add-ons
@description('Whether web app routing (preview) add-on is enabled')
param webAppRoutingAddon bool = true
// Configure AKS add-ons
var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union(
addOns.omsAgent,
{
config: {
logAnalyticsWorkspaceResourceID: logAnalytics.id
}
}
) : {}
var addOnsConfig = union(
(!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {},
(!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {},
(!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {},
(!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {},
(!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {}
)
// Link to existing log analytics workspace when available
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) {
name: logAnalyticsName
}
var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType]
// Create the primary AKS cluster resources and system node pool
module managedCluster 'aks-managed-cluster.bicep' = {
name: 'managed-cluster'
params: {
name: name
location: location
tags: tags
systemPoolConfig: union(
{ name: 'npsystem', mode: 'System' },
nodePoolBase,
systemPoolSpec
)
nodeResourceGroupName: nodeResourceGroupName
sku: sku
dnsPrefix: dnsPrefix
kubernetesVersion: kubernetesVersion
addOns: addOnsConfig
workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : ''
enableAad: enableAzureRbac && aadTenantId != ''
disableLocalAccounts: disableLocalAccounts
aadTenantId: aadTenantId
enableRbac: enableRbac
enableAzureRbac: enableAzureRbac
webAppRoutingAddon: webAppRoutingAddon
loadBalancerSku: loadBalancerSku
networkPlugin: networkPlugin
networkPolicy: networkPolicy
}
}
var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType)
var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType]
// Create additional user agent pool when specified
module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) {
name: 'aks-node-pool'
params: {
clusterName: managedCluster.outputs.clusterName
name: 'npuserpool'
config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec)
}
}
// Creates container registry (ACR)
module containerRegistry 'container-registry.bicep' = {
name: 'container-registry'
params: {
name: containerRegistryName
location: location
tags: tags
workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : ''
}
}
// Grant ACR Pull access from cluster managed identity to container registry
module containerRegistryAccess '../security/registry-access.bicep' = {
name: 'cluster-container-registry-access'
params: {
containerRegistryName: containerRegistry.outputs.name
principalId: managedCluster.outputs.clusterIdentity.objectId
}
}
// Give AKS cluster access to the specified principal
module clusterAccess '../security/aks-managed-cluster-access.bicep' = if (!empty(principalId) && (enableAzureRbac || disableLocalAccounts)) {
name: 'cluster-access'
params: {
clusterName: managedCluster.outputs.clusterName
principalId: principalId
principalType: principalType
}
}
// Give the AKS Cluster access to KeyVault
module clusterKeyVaultAccess '../security/keyvault-access.bicep' = {
name: 'cluster-keyvault-access'
params: {
keyVaultName: keyVaultName
principalId: managedCluster.outputs.clusterIdentity.objectId
}
}
// Helpers for node pool configuration
var nodePoolBase = {
osType: 'Linux'
maxPods: 30
type: 'VirtualMachineScaleSets'
upgradeSettings: {
maxSurge: '33%'
}
}
var nodePoolPresets = {
CostOptimised: {
vmSize: 'Standard_B4ms'
count: 1
minCount: 1
maxCount: 3
enableAutoScaling: true
availabilityZones: []
}
Standard: {
vmSize: 'Standard_DS2_v2'
count: 3
minCount: 3
maxCount: 5
enableAutoScaling: true
availabilityZones: [
'1'
'2'
'3'
]
}
HighSpec: {
vmSize: 'Standard_D4s_v3'
count: 3
minCount: 3
maxCount: 5
enableAutoScaling: true
availabilityZones: [
'1'
'2'
'3'
]
}
}
// Module outputs
@description('The resource name of the AKS cluster')
output clusterName string = managedCluster.outputs.clusterName
@description('The AKS cluster identity')
output clusterIdentity object = managedCluster.outputs.clusterIdentity
@description('The resource name of the ACR')
output containerRegistryName string = containerRegistry.outputs.name
@description('The login server for the container registry')
output containerRegistryLoginServer string = containerRegistry.outputs.loginServer
================================================
FILE: infra/core/host/appservice-appsettings.bicep
================================================
metadata description = 'Updates app settings for an Azure App Service.'
@description('The name of the app service resource within the current resource group scope')
param name string
@description('The app settings to be applied to the app service')
@secure()
param appSettings object
resource appService 'Microsoft.Web/sites@2022-03-01' existing = {
name: name
}
resource settings 'Microsoft.Web/sites/config@2022-03-01' = {
name: 'appsettings'
parent: appService
properties: appSettings
}
================================================
FILE: infra/core/host/appservice.bicep
================================================
metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.'
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)
param logAnalyticsWorkspaceId string = ''
// 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 = ''
@secure()
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 = ''
param virtualNetworkSubnetId 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
virtualNetworkSubnetId: !empty(virtualNetworkSubnetId) ? virtualNetworkSubnetId : null
}
identity: { type: managedIdentity ? 'SystemAssigned' : 'None' }
resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = {
name: 'ftp'
properties: {
allow: false
}
}
resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = {
name: 'scm'
properties: {
allow: false
}
}
}
resource webAppDiagSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!(empty(logAnalyticsWorkspaceId))) {
name: '${appService.name}-diagnosticSettings'
scope: appService
properties: {
workspaceId: logAnalyticsWorkspaceId
logs: [
{
category: 'AppServiceHTTPLogs'
categoryGroup: null
enabled: true
retentionPolicy: {
days: 7
enabled: true
}
}
{
category: 'AppServiceConsoleLogs'
categoryGroup: null
enabled: true
retentionPolicy: {
days: 7
enabled: true
}
}
{
category: 'AppServiceAppLogs'
categoryGroup: null
enabled: true
retentionPolicy: {
days: 7
enabled: true
}
}
]
metrics: [
{
category: 'AllMetrics'
enabled: true
retentionPolicy: {
days: 7
enabled: true
}
}
]
}
}
// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially
// sites/web/config 'appsettings'
module configAppSettings 'appservice-appsettings.bicep' = {
name: '${name}-appSettings'
params: {
name: appService.name
appSettings: union(appSettings,
{
SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment)
ENABLE_ORYX_BUILD: string(enableOryxBuild)
},
runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {},
!empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {},
!empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {})
}
}
// sites/web/config 'logs'
resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = {
name: 'logs'
parent: appService
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-11-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
================================================
metadata description = 'Creates an Azure App Service plan.'
param name string
param location string = resourceGroup().location
param tags object = {}
param logAnalyticsWorkspaceId string = ''
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
}
}
resource appServicePlanDiagSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!(empty(logAnalyticsWorkspaceId))) {
name: '${appServicePlan.name}-diagnosticSettings'
scope: appServicePlan
properties: {
workspaceId: logAnalyticsWorkspaceId
metrics: [
{
category: 'AllMetrics'
enabled: true
retentionPolicy: {
days: 7
enabled: true
}
}
]
}
}
output id string = appServicePlan.id
output name string = appServicePlan.name
================================================
FILE: infra/core/host/container-app-upsert.bicep
================================================
metadata description = 'Creates or updates an existing Azure Container App.'
param name string
param location string = resourceGroup().location
param tags object = {}
@description('The environment name for the container apps')
param containerAppsEnvironmentName string
@description('The number of CPU cores allocated to a single container instance, e.g., 0.5')
param containerCpuCoreCount string = '0.5'
@description('The maximum number of replicas to run. Must be at least 1.')
@minValue(1)
param containerMaxReplicas int = 10
@description('The amount of memory allocated to a single container instance, e.g., 1Gi')
param containerMemory string = '1.0Gi'
@description('The minimum number of replicas to run. Must be at least 1.')
@minValue(1)
param containerMinReplicas int = 1
@description('The name of the container')
param containerName string = 'main'
@description('The name of the container registry')
param containerRegistryName string = ''
@description('Hostname suffix for container registry. Set when deploying to sovereign clouds')
param containerRegistryHostSuffix string = 'azurecr.io'
@allowed([ 'http', 'grpc' ])
@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC')
param daprAppProtocol string = 'http'
@description('Enable or disable Dapr for the container app')
param daprEnabled bool = false
@description('The Dapr app ID')
param daprAppId string = containerName
@description('Specifies if the resource already exists')
param exists bool = false
@description('Specifies if Ingress is enabled for the container app')
param ingressEnabled bool = true
@description('The type of identity for the resource')
@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ])
param identityType string = 'None'
@description('The name of the user-assigned identity')
param identityName string = ''
@description('The name of the container image')
param imageName string = ''
@description('The secrets required for the container')
@secure()
param secrets object = {}
@description('The environment variables for the container')
param env array = []
@description('Specifies if the resource ingress is exposed externally')
param external bool = true
@description('The service binds associated with the container')
param serviceBinds array = []
@description('The target port for the container')
param targetPort int = 80
resource existingApp 'Microsoft.App/containerApps@2023-05-02-preview' existing = if (exists) {
name: name
}
module app 'container-app.bicep' = {
name: '${deployment().name}-update'
params: {
name: name
location: location
tags: tags
identityType: identityType
identityName: identityName
ingressEnabled: ingressEnabled
containerName: containerName
containerAppsEnvironmentName: containerAppsEnvironmentName
containerRegistryName: containerRegistryName
containerRegistryHostSuffix: containerRegistryHostSuffix
containerCpuCoreCount: containerCpuCoreCount
containerMemory: containerMemory
containerMinReplicas: containerMinReplicas
containerMaxReplicas: containerMaxReplicas
daprEnabled: daprEnabled
daprAppId: daprAppId
daprAppProtocol: daprAppProtocol
secrets: secrets
external: external
env: env
imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : ''
targetPort: targetPort
serviceBinds: serviceBinds
}
}
output defaultDomain string = app.outputs.defaultDomain
output imageName string = app.outputs.imageName
output name string = app.outputs.name
output uri string = app.outputs.uri
================================================
FILE: infra/core/host/container-app.bicep
================================================
metadata description = 'Creates a container app in an Azure Container App environment.'
param name string
param location string = resourceGroup().location
param tags object = {}
@description('Allowed origins')
param allowedOrigins array = []
@description('Name of the environment for container apps')
param containerAppsEnvironmentName string
@description('CPU cores allocated to a single container instance, e.g., 0.5')
param containerCpuCoreCount string = '0.5'
@description('The maximum number of replicas to run. Must be at least 1.')
@minValue(1)
param containerMaxReplicas int = 10
@description('Memory allocated to a single container instance, e.g., 1Gi')
param containerMemory string = '1.0Gi'
@description('The minimum number of replicas to run. Must be at least 1.')
param containerMinReplicas int = 1
@description('The name of the container')
param containerName string = 'main'
@description('The name of the container registry')
param containerRegistryName string = ''
@description('Hostname suffix for container registry. Set when deploying to sovereign clouds')
param containerRegistryHostSuffix string = 'azurecr.io'
@description('The protocol used by Dapr to connect to the app, e.g., http or grpc')
@allowed([ 'http', 'grpc' ])
param daprAppProtocol string = 'http'
@description('The Dapr app ID')
param daprAppId string = containerName
@description('Enable Dapr')
param daprEnabled bool = false
@description('The environment variables for the container')
param env array = []
@description('Specifies if the resource ingress is exposed externally')
param external bool = true
@description('The name of the user-assigned identity')
param identityName string = ''
@description('The type of identity for the resource')
@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ])
param identityType string = 'None'
@description('The name of the container image')
param imageName string = ''
@description('Specifies if Ingress is enabled for the container app')
param ingressEnabled bool = true
param revisionMode string = 'Single'
@description('The secrets required for the container')
@secure()
param secrets object = {}
@description('The service binds associated with the container')
param serviceBinds array = []
@description('The name of the container apps add-on to use. e.g. redis')
param serviceType string = ''
@description('The target port for the container')
param targetPort int = 80
resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) {
name: identityName
}
// Private registry support requires both an ACR name and a User Assigned managed identity
var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName)
// Automatically set to `UserAssigned` when an `identityName` has been set
var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType
module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) {
name: '${deployment().name}-registry-access'
params: {
containerRegistryName: containerRegistryName
principalId: usePrivateRegistry ? userIdentity.properties.principalId : ''
}
}
resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
name: name
location: location
tags: tags
// It is critical that the identity is granted ACR pull access before the app is created
// otherwise the container app will throw a provision error
// This also forces us to use an user assigned managed identity since there would no way to
// provide the system assigned identity with the ACR pull access before the app is created
dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : []
identity: {
type: normalizedIdentityType
userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null
}
properties: {
managedEnvironmentId: containerAppsEnvironment.id
configuration: {
activeRevisionsMode: revisionMode
ingress: ingressEnabled ? {
external: external
targetPort: targetPort
transport: 'auto'
corsPolicy: {
allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins)
}
} : null
dapr: daprEnabled ? {
enabled: true
appId: daprAppId
appProtocol: daprAppProtocol
appPort: ingressEnabled ? targetPort : 0
} : { enabled: false }
secrets: [for secret in items(secrets): {
name: secret.key
value: secret.value
}]
service: !empty(serviceType) ? { type: serviceType } : null
registries: usePrivateRegistry ? [
{
server: '${containerRegistryName}.${containerRegistryHostSuffix}'
identity: userIdentity.id
}
] : []
}
template: {
serviceBinds: !empty(serviceBinds) ? serviceBinds : null
containers: [
{
image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
name: containerName
env: env
resources: {
cpu: json(containerCpuCoreCount)
memory: containerMemory
}
}
]
scale: {
minReplicas: containerMinReplicas
maxReplicas: containerMaxReplicas
}
}
}
}
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = {
name: containerAppsEnvironmentName
}
output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId)
output imageName string = imageName
output name string = app.name
output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {}
output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : ''
================================================
FILE: infra/core/host/container-apps-environment.bicep
================================================
metadata description = 'Creates an Azure Container Apps environment.'
param name string
param location string = resourceGroup().location
param tags object = {}
@description('Name of the Application Insights resource')
param applicationInsightsName string = ''
@description('Specifies if Dapr is enabled')
param daprEnabled bool = false
@description('Name of the Log Analytics workspace')
param logAnalyticsWorkspaceName string
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
name: name
location: location
tags: tags
properties: {
appLogsConfiguration: {
destination: 'log-analytics'
logAnalyticsConfiguration: {
customerId: logAnalyticsWorkspace.properties.customerId
sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
}
}
daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : ''
}
}
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: logAnalyticsWorkspaceName
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) {
name: applicationInsightsName
}
output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
output id string = containerAppsEnvironment.id
output name string = containerAppsEnvironment.name
================================================
FILE: infra/core/host/container-apps.bicep
================================================
metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.'
param name string
param location string = resourceGroup().location
param tags object = {}
param containerAppsEnvironmentName string
param containerRegistryName string
param containerRegistryResourceGroupName string = ''
param containerRegistryAdminUserEnabled bool = false
param logAnalyticsWorkspaceName string
param applicationInsightsName string = ''
param daprEnabled bool = false
module containerAppsEnvironment 'container-apps-environment.bicep' = {
name: '${name}-container-apps-environment'
params: {
name: containerAppsEnvironmentName
location: location
tags: tags
logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
applicationInsightsName: applicationInsightsName
daprEnabled: daprEnabled
}
}
module containerRegistry 'container-registry.bicep' = {
name: '${name}-container-registry'
scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup()
params: {
name: containerRegistryName
location: location
adminUserEnabled: containerRegistryAdminUserEnabled
tags: tags
}
}
output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain
output environmentName string = containerAppsEnvironment.outputs.name
output environmentId string = containerAppsEnvironment.outputs.id
output registryLoginServer string = containerRegistry.outputs.loginServer
output registryName string = containerRegistry.outputs.name
================================================
FILE: infra/core/host/container-registry.bicep
================================================
metadata description = 'Creates an Azure Container Registry.'
param name string
param location string = resourceGroup().location
param tags object = {}
@description('Indicates whether admin user is enabled')
param adminUserEnabled bool = false
@description('Indicates whether anonymous pull is enabled')
param anonymousPullEnabled bool = false
@description('Azure ad authentication as arm policy settings')
param azureADAuthenticationAsArmPolicy object = {
status: 'enabled'
}
@description('Indicates whether data endpoint is enabled')
param dataEndpointEnabled bool = false
@description('Encryption settings')
param encryption object = {
status: 'disabled'
}
@description('Export policy settings')
param exportPolicy object = {
status: 'enabled'
}
@description('Metadata search settings')
param metadataSearch string = 'Disabled'
@description('Options for bypassing network rules')
param networkRuleBypassOptions string = 'AzureServices'
@description('Public network access setting')
param publicNetworkAccess string = 'Enabled'
@description('Quarantine policy settings')
param quarantinePolicy object = {
status: 'disabled'
}
@description('Retention policy settings')
param retentionPolicy object = {
days: 7
status: 'disabled'
}
@description('Scope maps setting')
param scopeMaps array = []
@description('SKU settings')
param sku object = {
name: 'Basic'
}
@description('Soft delete policy settings')
param softDeletePolicy object = {
retentionDays: 7
status: 'disabled'
}
@description('Trust policy settings')
param trustPolicy object = {
type: 'Notary'
status: 'disabled'
}
@description('Zone redundancy setting')
param zoneRedundancy string = 'Disabled'
@description('The log analytics workspace ID used for logging and monitoring')
param workspaceId string = ''
// 2023-11-01-preview needed for metadataSearch
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-11-01-preview' = {
name: name
location: location
tags: tags
sku: sku
properties: {
adminUserEnabled: adminUserEnabled
anonymousPullEnabled: anonymousPullEnabled
dataEndpointEnabled: dataEndpointEnabled
encryption: encryption
metadataSearch: metadataSearch
networkRuleBypassOptions: networkRuleBypassOptions
policies:{
quarantinePolicy: quarantinePolicy
trustPolicy: trustPolicy
retentionPolicy: retentionPolicy
exportPolicy: exportPolicy
azureADAuthenticationAsArmPolicy: azureADAuthenticationAsArmPolicy
softDeletePolicy: softDeletePolicy
}
publicNetworkAccess: publicNetworkAccess
zoneRedundancy: zoneRedundancy
}
resource scopeMap 'scopeMaps' = [for scopeMap in scopeMaps: {
name: scopeMap.name
properties: scopeMap.properties
}]
}
// TODO: Update diagnostics to be its own module
// Blocking issue: https://github.com/Azure/bicep/issues/622
// Unable to pass in a `resource` scope or unable to use string interpolation in resource types
resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) {
name: 'registry-diagnostics'
scope: containerRegistry
properties: {
workspaceId: workspaceId
logs: [
{
category: 'ContainerRegistryRepositoryEvents'
enabled: true
}
{
category: 'ContainerRegistryLoginEvents'
enabled: true
}
]
metrics: [
{
category: 'AllMetrics'
enabled: true
timeGrain: 'PT1M'
}
]
}
}
output id string = containerRegistry.id
output loginServer string = containerRegistry.properties.loginServer
output name string = containerRegistry.name
================================================
FILE: infra/core/host/functions.bicep
================================================
metadata description = 'Creates an Azure Function in an existing Azure App Service plan.'
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) || storageManagedIdentity
param storageAccountName string
param storageManagedIdentity bool = false
param virtualNetworkSubnetId string = ''
// Runtime Properties
@allowed([
'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom'
])
param runtimeName string
param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}'
param runtimeVersion string
// Function Settings
@allowed([
'~4', '~3', '~2', '~1'
])
param extensionVersion string = '~4'
// Microsoft.Web/sites Properties
param kind string = 'functionapp,linux'
// Microsoft.Web/sites/config
param allowedOrigins array = []
param alwaysOn bool = true
param appCommandLine string = ''
@secure()
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 = true
param use32BitWorkerProcess bool = false
param healthCheckPath string = ''
module functions 'appservice.bicep' = {
name: '${name}-functions'
params: {
name: name
location: location
tags: tags
allowedOrigins: allowedOrigins
alwaysOn: alwaysOn
appCommandLine: appCommandLine
applicationInsightsName: applicationInsightsName
appServicePlanId: appServicePlanId
appSettings: union(appSettings, {
AzureWebJobsStorage__accountName: storageManagedIdentity ? storage.name : null
AzureWebJobsStorage: storageManagedIdentity ? null : 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
FUNCTIONS_EXTENSION_VERSION: extensionVersion
FUNCTIONS_WORKER_RUNTIME: runtimeName
})
clientAffinityEnabled: clientAffinityEnabled
enableOryxBuild: enableOryxBuild
functionAppScaleLimit: functionAppScaleLimit
healthCheckPath: healthCheckPath
keyVaultName: keyVaultName
kind: kind
linuxFxVersion: linuxFxVersion
managedIdentity: managedIdentity
minimumElasticInstanceCount: minimumElasticInstanceCount
numberOfWorkers: numberOfWorkers
runtimeName: runtimeName
runtimeVersion: runtimeVersion
runtimeNameAndVersion: runtimeNameAndVersion
scmDoBuildDuringDeployment: scmDoBuildDuringDeployment
use32BitWorkerProcess: use32BitWorkerProcess
virtualNetworkSubnetId: virtualNetworkSubnetId
}
}
module storageOwnerRole '../../core/security/role.bicep' = if (storageManagedIdentity) {
name: 'search-index-contrib-role-api'
params: {
principalId: functions.outputs.identityPrincipalId
// Search Index Data Contributor
roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7'
principalType: 'ServicePrincipal'
}
}
resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = {
name: storageAccountName
}
output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : ''
output name string = functions.outputs.name
output uri string = functions.outputs.uri
================================================
FILE: infra/core/host/staticwebapp.bicep
================================================
metadata description = 'Creates an Azure Static Web Apps instance.'
param name string
param location string = resourceGroup().location
param tags object = {}
param sku object = {
name: 'Free'
tier: 'Free'
}
resource web 'Microsoft.Web/staticSites@2022-03-01' = {
name: name
location: location
tags: tags
sku: sku
properties: {
provider: 'Custom'
}
}
output name string = web.name
output uri string = 'https://${web.properties.defaultHostname}'
================================================
FILE: infra/core/monitor/applicationinsights-dashboard.bicep
================================================
metadata description = 'Creates a dashboard for an Application Insights instance.'
param name string
param applicationInsightsName string
param location string = resourceGroup().location
param tags object = {}
// 2020-09-01-preview because that is the latest valid version
resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = {
name: name
location: location
tags: tags
properties: {
lenses: [
{
order: 0
parts: [
{
position: {
x: 0
y: 0
colSpan: 2
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'id'
value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
{
name: 'Version'
value: '1.0'
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart'
asset: {
idInputName: 'id'
type: 'ApplicationInsights'
}
defaultMenuItemId: 'overview'
}
}
{
position: {
x: 2
y: 0
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ComponentId'
value: {
Name: applicationInsights.name
SubscriptionId: subscription().subscriptionId
ResourceGroup: resourceGroup().name
}
}
{
name: 'Version'
value: '1.0'
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart'
asset: {
idInputName: 'ComponentId'
type: 'ApplicationInsights'
}
defaultMenuItemId: 'ProactiveDetection'
}
}
{
position: {
x: 3
y: 0
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ComponentId'
value: {
Name: applicationInsights.name
SubscriptionId: subscription().subscriptionId
ResourceGroup: resourceGroup().name
}
}
{
name: 'ResourceId'
value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart'
asset: {
idInputName: 'ComponentId'
type: 'ApplicationInsights'
}
}
}
{
position: {
x: 4
y: 0
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ComponentId'
value: {
Name: applicationInsights.name
SubscriptionId: subscription().subscriptionId
ResourceGroup: resourceGroup().name
}
}
{
name: 'TimeContext'
value: {
durationMs: 86400000
endTime: null
createdTime: '2018-05-04T01:20:33.345Z'
isInitialTime: true
grain: 1
useDashboardTimeRange: false
}
}
{
name: 'Version'
value: '1.0'
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart'
asset: {
idInputName: 'ComponentId'
type: 'ApplicationInsights'
}
}
}
{
position: {
x: 5
y: 0
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ComponentId'
value: {
Name: applicationInsights.name
SubscriptionId: subscription().subscriptionId
ResourceGroup: resourceGroup().name
}
}
{
name: 'TimeContext'
value: {
durationMs: 86400000
endTime: null
createdTime: '2018-05-08T18:47:35.237Z'
isInitialTime: true
grain: 1
useDashboardTimeRange: false
}
}
{
name: 'ConfigurationId'
value: '78ce933e-e864-4b05-a27b-71fd55a6afad'
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart'
asset: {
idInputName: 'ComponentId'
type: 'ApplicationInsights'
}
}
}
{
position: {
x: 0
y: 1
colSpan: 3
rowSpan: 1
}
metadata: {
inputs: []
type: 'Extension/HubsExtension/PartType/MarkdownPart'
settings: {
content: {
settings: {
content: '# Usage'
title: ''
subtitle: ''
}
}
}
}
}
{
position: {
x: 3
y: 1
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ComponentId'
value: {
Name: applicationInsights.name
SubscriptionId: subscription().subscriptionId
ResourceGroup: resourceGroup().name
}
}
{
name: 'TimeContext'
value: {
durationMs: 86400000
endTime: null
createdTime: '2018-05-04T01:22:35.782Z'
isInitialTime: true
grain: 1
useDashboardTimeRange: false
}
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart'
asset: {
idInputName: 'ComponentId'
type: 'ApplicationInsights'
}
}
}
{
position: {
x: 4
y: 1
colSpan: 3
rowSpan: 1
}
metadata: {
inputs: []
type: 'Extension/HubsExtension/PartType/MarkdownPart'
settings: {
content: {
settings: {
content: '# Reliability'
title: ''
subtitle: ''
}
}
}
}
}
{
position: {
x: 7
y: 1
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ResourceId'
value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
{
name: 'DataModel'
value: {
version: '1.0.0'
timeContext: {
durationMs: 86400000
createdTime: '2018-05-04T23:42:40.072Z'
isInitialTime: false
grain: 1
useDashboardTimeRange: false
}
}
isOptional: true
}
{
name: 'ConfigurationId'
value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f'
isOptional: true
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart'
isAdapter: true
asset: {
idInputName: 'ResourceId'
type: 'ApplicationInsights'
}
defaultMenuItemId: 'failures'
}
}
{
position: {
x: 8
y: 1
colSpan: 3
rowSpan: 1
}
metadata: {
inputs: []
type: 'Extension/HubsExtension/PartType/MarkdownPart'
settings: {
content: {
settings: {
content: '# Responsiveness\r\n'
title: ''
subtitle: ''
}
}
}
}
}
{
position: {
x: 11
y: 1
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ResourceId'
value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
{
name: 'DataModel'
value: {
version: '1.0.0'
timeContext: {
durationMs: 86400000
createdTime: '2018-05-04T23:43:37.804Z'
isInitialTime: false
grain: 1
useDashboardTimeRange: false
}
}
isOptional: true
}
{
name: 'ConfigurationId'
value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865'
isOptional: true
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart'
isAdapter: true
asset: {
idInputName: 'ResourceId'
type: 'ApplicationInsights'
}
defaultMenuItemId: 'performance'
}
}
{
position: {
x: 12
y: 1
colSpan: 3
rowSpan: 1
}
metadata: {
inputs: []
type: 'Extension/HubsExtension/PartType/MarkdownPart'
settings: {
content: {
settings: {
content: '# Browser'
title: ''
subtitle: ''
}
}
}
}
}
{
position: {
x: 15
y: 1
colSpan: 1
rowSpan: 1
}
metadata: {
inputs: [
{
name: 'ComponentId'
value: {
Name: applicationInsights.name
SubscriptionId: subscription().subscriptionId
ResourceGroup: resourceGroup().name
}
}
{
name: 'MetricsExplorerJsonDefinitionId'
value: 'BrowserPerformanceTimelineMetrics'
}
{
name: 'TimeContext'
value: {
durationMs: 86400000
createdTime: '2018-05-08T12:16:27.534Z'
isInitialTime: false
grain: 1
useDashboardTimeRange: false
}
}
{
name: 'CurrentFilter'
value: {
eventTypes: [
4
1
3
5
2
6
13
]
typeFacets: {}
isPermissive: false
}
}
{
name: 'id'
value: {
Name: applicationInsights.name
SubscriptionId: subscription().subscriptionId
ResourceGroup: resourceGroup().name
}
}
{
name: 'Version'
value: '1.0'
}
]
#disable-next-line BCP036
type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart'
asset: {
idInputName: 'ComponentId'
type: 'ApplicationInsights'
}
defaultMenuItemId: 'browser'
}
}
{
position: {
x: 0
y: 2
colSpan: 4
rowSpan: 3
}
metadata: {
inputs: [
{
name: 'options'
value: {
chart: {
metrics: [
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'sessions/count'
aggregationType: 5
namespace: 'microsoft.insights/components/kusto'
metricVisualization: {
displayName: 'Sessions'
color: '#47BDF5'
}
}
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'users/count'
aggregationType: 5
namespace: 'microsoft.insights/components/kusto'
metricVisualization: {
displayName: 'Users'
color: '#7E58FF'
}
}
]
title: 'Unique sessions and users'
visualization: {
chartType: 2
legendVisualization: {
isVisible: true
position: 2
hideSubtitle: false
}
axisVisualization: {
x: {
isVisible: true
axisType: 2
}
y: {
isVisible: true
axisType: 1
}
}
}
openBladeOnClick: {
openBlade: true
destinationBlade: {
extensionName: 'HubsExtension'
bladeName: 'ResourceMenuBlade'
parameters: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
menuid: 'segmentationUsers'
}
}
}
}
}
}
{
name: 'sharedTimeRange'
isOptional: true
}
]
#disable-next-line BCP036
type: 'Extension/HubsExtension/PartType/MonitorChartPart'
settings: {}
}
}
{
position: {
x: 4
y: 2
colSpan: 4
rowSpan: 3
}
metadata: {
inputs: [
{
name: 'options'
value: {
chart: {
metrics: [
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'requests/failed'
aggregationType: 7
namespace: 'microsoft.insights/components'
metricVisualization: {
displayName: 'Failed requests'
color: '#EC008C'
}
}
]
title: 'Failed requests'
visualization: {
chartType: 3
legendVisualization: {
isVisible: true
position: 2
hideSubtitle: false
}
axisVisualization: {
x: {
isVisible: true
axisType: 2
}
y: {
isVisible: true
axisType: 1
}
}
}
openBladeOnClick: {
openBlade: true
destinationBlade: {
extensionName: 'HubsExtension'
bladeName: 'ResourceMenuBlade'
parameters: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
menuid: 'failures'
}
}
}
}
}
}
{
name: 'sharedTimeRange'
isOptional: true
}
]
#disable-next-line BCP036
type: 'Extension/HubsExtension/PartType/MonitorChartPart'
settings: {}
}
}
{
position: {
x: 8
y: 2
colSpan: 4
rowSpan: 3
}
metadata: {
inputs: [
{
name: 'options'
value: {
chart: {
metrics: [
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'requests/duration'
aggregationType: 4
namespace: 'microsoft.insights/components'
metricVisualization: {
displayName: 'Server response time'
color: '#00BCF2'
}
}
]
title: 'Server response time'
visualization: {
chartType: 2
legendVisualization: {
isVisible: true
position: 2
hideSubtitle: false
}
axisVisualization: {
x: {
isVisible: true
axisType: 2
}
y: {
isVisible: true
axisType: 1
}
}
}
openBladeOnClick: {
openBlade: true
destinationBlade: {
extensionName: 'HubsExtension'
bladeName: 'ResourceMenuBlade'
parameters: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
menuid: 'performance'
}
}
}
}
}
}
{
name: 'sharedTimeRange'
isOptional: true
}
]
#disable-next-line BCP036
type: 'Extension/HubsExtension/PartType/MonitorChartPart'
settings: {}
}
}
{
position: {
x: 12
y: 2
colSpan: 4
rowSpan: 3
}
metadata: {
inputs: [
{
name: 'options'
value: {
chart: {
metrics: [
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'browserTimings/networkDuration'
aggregationType: 4
namespace: 'microsoft.insights/components'
metricVisualization: {
displayName: 'Page load network connect time'
color: '#7E58FF'
}
}
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'browserTimings/processingDuration'
aggregationType: 4
namespace: 'microsoft.insights/components'
metricVisualization: {
displayName: 'Client processing time'
color: '#44F1C8'
}
}
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'browserTimings/sendDuration'
aggregationType: 4
namespace: 'microsoft.insights/components'
metricVisualization: {
displayName: 'Send request time'
color: '#EB9371'
}
}
{
resourceMetadata: {
id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}'
}
name: 'browserTimings/receiveDuration'
aggregationType: 4
gitextract_y908z1c3/
├── .aspire/
│ └── settings.json
├── .azdo/
│ └── pipelines/
│ └── azure-dev.yml
├── .devcontainer/
│ ├── README.md
│ └── devcontainer.json
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ └── workflows/
│ ├── azure-dev.yml
│ ├── build.yml
│ ├── codeql.yml
│ ├── release.yml
│ └── test-templates.yml
├── .gitignore
├── .template.config/
│ ├── dotnetcli.host.json
│ ├── ide.host.json
│ └── template.json
├── CODE_OF_CONDUCT.md
├── CleanArchitecture.nuspec
├── CleanArchitecture.slnx
├── Directory.Build.props
├── Directory.Packages.props
├── LICENSE
├── README-template.md
├── README.md
├── azure.yaml
├── build/
│ ├── build.ps1
│ ├── repack.ps1
│ └── test.ps1
├── global.json
├── infra/
│ ├── README.md
│ ├── abbreviations.json
│ ├── core/
│ │ ├── ai/
│ │ │ ├── cognitiveservices.bicep
│ │ │ ├── hub-dependencies.bicep
│ │ │ ├── hub.bicep
│ │ │ └── project.bicep
│ │ ├── config/
│ │ │ └── configstore.bicep
│ │ ├── database/
│ │ │ ├── cosmos/
│ │ │ │ ├── cosmos-account.bicep
│ │ │ │ ├── mongo/
│ │ │ │ │ ├── cosmos-mongo-account.bicep
│ │ │ │ │ └── cosmos-mongo-db.bicep
│ │ │ │ └── sql/
│ │ │ │ ├── cosmos-sql-account.bicep
│ │ │ │ ├── cosmos-sql-db.bicep
│ │ │ │ ├── cosmos-sql-role-assign.bicep
│ │ │ │ └── cosmos-sql-role-def.bicep
│ │ │ ├── mysql/
│ │ │ │ └── flexibleserver.bicep
│ │ │ ├── postgresql/
│ │ │ │ └── flexibleserver.bicep
│ │ │ └── sqlserver/
│ │ │ └── sqlserver.bicep
│ │ ├── gateway/
│ │ │ └── apim.bicep
│ │ ├── host/
│ │ │ ├── ai-environment.bicep
│ │ │ ├── aks-agent-pool.bicep
│ │ │ ├── aks-managed-cluster.bicep
│ │ │ ├── aks.bicep
│ │ │ ├── appservice-appsettings.bicep
│ │ │ ├── appservice.bicep
│ │ │ ├── appserviceplan.bicep
│ │ │ ├── container-app-upsert.bicep
│ │ │ ├── container-app.bicep
│ │ │ ├── container-apps-environment.bicep
│ │ │ ├── container-apps.bicep
│ │ │ ├── container-registry.bicep
│ │ │ ├── functions.bicep
│ │ │ └── staticwebapp.bicep
│ │ ├── monitor/
│ │ │ ├── applicationinsights-dashboard.bicep
│ │ │ ├── applicationinsights.bicep
│ │ │ ├── loganalytics.bicep
│ │ │ └── monitoring.bicep
│ │ ├── networking/
│ │ │ ├── cdn-endpoint.bicep
│ │ │ ├── cdn-profile.bicep
│ │ │ └── cdn.bicep
│ │ ├── search/
│ │ │ └── search-services.bicep
│ │ ├── security/
│ │ │ ├── aks-managed-cluster-access.bicep
│ │ │ ├── configstore-access.bicep
│ │ │ ├── keyvault-access.bicep
│ │ │ ├── keyvault-secret.bicep
│ │ │ ├── keyvault.bicep
│ │ │ ├── registry-access.bicep
│ │ │ └── role.bicep
│ │ ├── storage/
│ │ │ └── storage-account.bicep
│ │ └── testing/
│ │ └── loadtesting.bicep
│ ├── main.bicep
│ ├── main.parameters.json
│ └── services/
│ └── web.bicep
├── renovate.json
├── src/
│ ├── AppHost/
│ │ ├── AppHost.csproj
│ │ ├── Program.cs
│ │ ├── Properties/
│ │ │ └── launchSettings.json
│ │ ├── appsettings.Development.json
│ │ └── appsettings.json
│ ├── Application/
│ │ ├── Application.csproj
│ │ ├── Common/
│ │ │ ├── Behaviours/
│ │ │ │ ├── AuthorizationBehaviour.cs
│ │ │ │ ├── LoggingBehaviour.cs
│ │ │ │ ├── PerformanceBehaviour.cs
│ │ │ │ ├── UnhandledExceptionBehaviour.cs
│ │ │ │ └── ValidationBehaviour.cs
│ │ │ ├── Exceptions/
│ │ │ │ ├── ForbiddenAccessException.cs
│ │ │ │ └── ValidationException.cs
│ │ │ ├── Interfaces/
│ │ │ │ ├── IApplicationDbContext.cs
│ │ │ │ ├── IIdentityService.cs
│ │ │ │ └── IUser.cs
│ │ │ ├── Mappings/
│ │ │ │ └── MappingExtensions.cs
│ │ │ ├── Models/
│ │ │ │ ├── LookupDto.cs
│ │ │ │ ├── PaginatedList.cs
│ │ │ │ └── Result.cs
│ │ │ └── Security/
│ │ │ └── AuthorizeAttribute.cs
│ │ ├── DependencyInjection.cs
│ │ ├── GlobalUsings.cs
│ │ ├── TodoItems/
│ │ │ ├── Commands/
│ │ │ │ ├── CreateTodoItem/
│ │ │ │ │ ├── CreateTodoItem.cs
│ │ │ │ │ └── CreateTodoItemCommandValidator.cs
│ │ │ │ ├── DeleteTodoItem/
│ │ │ │ │ └── DeleteTodoItem.cs
│ │ │ │ ├── UpdateTodoItem/
│ │ │ │ │ ├── UpdateTodoItem.cs
│ │ │ │ │ └── UpdateTodoItemCommandValidator.cs
│ │ │ │ └── UpdateTodoItemDetail/
│ │ │ │ └── UpdateTodoItemDetail.cs
│ │ │ ├── EventHandlers/
│ │ │ │ ├── LogTodoItemCompleted.cs
│ │ │ │ └── LogTodoItemCreated.cs
│ │ │ └── Queries/
│ │ │ └── GetTodoItemsWithPagination/
│ │ │ ├── GetTodoItemsWithPagination.cs
│ │ │ ├── GetTodoItemsWithPaginationQueryValidator.cs
│ │ │ └── TodoItemBriefDto.cs
│ │ ├── TodoLists/
│ │ │ ├── Commands/
│ │ │ │ ├── CreateTodoList/
│ │ │ │ │ ├── CreateTodoList.cs
│ │ │ │ │ └── CreateTodoListCommandValidator.cs
│ │ │ │ ├── DeleteTodoList/
│ │ │ │ │ └── DeleteTodoList.cs
│ │ │ │ ├── PurgeTodoLists/
│ │ │ │ │ └── PurgeTodoLists.cs
│ │ │ │ └── UpdateTodoList/
│ │ │ │ ├── UpdateTodoList.cs
│ │ │ │ └── UpdateTodoListCommandValidator.cs
│ │ │ └── Queries/
│ │ │ └── GetTodos/
│ │ │ ├── GetTodos.cs
│ │ │ ├── TodoItemDto.cs
│ │ │ ├── TodoListDto.cs
│ │ │ └── TodosVm.cs
│ │ └── WeatherForecasts/
│ │ └── Queries/
│ │ └── GetWeatherForecasts/
│ │ ├── GetWeatherForecastsQuery.cs
│ │ └── WeatherForecast.cs
│ ├── Domain/
│ │ ├── Common/
│ │ │ ├── BaseAuditableEntity.cs
│ │ │ ├── BaseEntity.cs
│ │ │ ├── BaseEvent.cs
│ │ │ └── ValueObject.cs
│ │ ├── Constants/
│ │ │ ├── Policies.cs
│ │ │ └── Roles.cs
│ │ ├── Domain.csproj
│ │ ├── Entities/
│ │ │ ├── TodoItem.cs
│ │ │ └── TodoList.cs
│ │ ├── Enums/
│ │ │ └── PriorityLevel.cs
│ │ ├── Events/
│ │ │ ├── TodoItemCompletedEvent.cs
│ │ │ ├── TodoItemCreatedEvent.cs
│ │ │ └── TodoItemDeletedEvent.cs
│ │ ├── Exceptions/
│ │ │ └── UnsupportedColourException.cs
│ │ ├── GlobalUsings.cs
│ │ └── ValueObjects/
│ │ └── Colour.cs
│ ├── Infrastructure/
│ │ ├── Data/
│ │ │ ├── ApplicationDbContext.cs
│ │ │ ├── ApplicationDbContextInitialiser.cs
│ │ │ ├── Configurations/
│ │ │ │ ├── TodoItemConfiguration.cs
│ │ │ │ └── TodoListConfiguration.cs
│ │ │ └── Interceptors/
│ │ │ ├── AuditableEntityInterceptor.cs
│ │ │ └── DispatchDomainEventsInterceptor.cs
│ │ ├── DependencyInjection.cs
│ │ ├── GlobalUsings.cs
│ │ ├── Identity/
│ │ │ ├── ApplicationUser.cs
│ │ │ ├── IdentityResultExtensions.cs
│ │ │ └── IdentityService.cs
│ │ └── Infrastructure.csproj
│ ├── ServiceDefaults/
│ │ ├── Extensions.cs
│ │ └── ServiceDefaults.csproj
│ ├── Shared/
│ │ ├── Services.cs
│ │ └── Shared.csproj
│ └── Web/
│ ├── ClientApp/
│ │ ├── .editorconfig
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── angular.json
│ │ ├── default.conf.template
│ │ ├── karma.conf.js
│ │ ├── nswag.json
│ │ ├── package.json
│ │ ├── proxy.conf.js
│ │ ├── src/
│ │ │ ├── api-authorization/
│ │ │ │ ├── auth.guard.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── authorize.interceptor.spec.ts
│ │ │ │ ├── authorize.interceptor.ts
│ │ │ │ ├── login/
│ │ │ │ │ ├── login.component.html
│ │ │ │ │ └── login.component.ts
│ │ │ │ └── register/
│ │ │ │ ├── register.component.html
│ │ │ │ └── register.component.ts
│ │ │ ├── app/
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── app.server.module.ts
│ │ │ │ ├── counter/
│ │ │ │ │ ├── counter.component.html
│ │ │ │ │ ├── counter.component.spec.ts
│ │ │ │ │ └── counter.component.ts
│ │ │ │ ├── fetch-data/
│ │ │ │ │ ├── fetch-data.component.html
│ │ │ │ │ └── fetch-data.component.ts
│ │ │ │ ├── home/
│ │ │ │ │ ├── home.component.html
│ │ │ │ │ └── home.component.ts
│ │ │ │ ├── nav-menu/
│ │ │ │ │ ├── nav-menu.component.html
│ │ │ │ │ ├── nav-menu.component.scss
│ │ │ │ │ └── nav-menu.component.ts
│ │ │ │ ├── theme-toggle/
│ │ │ │ │ ├── theme-toggle.component.html
│ │ │ │ │ └── theme-toggle.component.ts
│ │ │ │ ├── theme.service.ts
│ │ │ │ ├── todo/
│ │ │ │ │ ├── todo.component.html
│ │ │ │ │ ├── todo.component.scss
│ │ │ │ │ └── todo.component.ts
│ │ │ │ └── weather/
│ │ │ │ ├── weather.component.html
│ │ │ │ └── weather.component.ts
│ │ │ ├── assets/
│ │ │ │ └── .gitkeep
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── styles.scss
│ │ │ └── test.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ └── tsconfig.spec.json
│ ├── ClientApp-React/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── aspnetcore-https.cjs
│ │ ├── index.html
│ │ ├── nswag.json
│ │ ├── package.json
│ │ ├── public/
│ │ │ └── manifest.webmanifest
│ │ ├── src/
│ │ │ ├── App.jsx
│ │ │ ├── AppRoutes.jsx
│ │ │ ├── components/
│ │ │ │ ├── Counter.jsx
│ │ │ │ ├── Home.jsx
│ │ │ │ ├── Layout.jsx
│ │ │ │ ├── NavMenu.jsx
│ │ │ │ ├── ThemeContext.jsx
│ │ │ │ ├── ThemeToggle.jsx
│ │ │ │ ├── Todo.jsx
│ │ │ │ ├── Weather.jsx
│ │ │ │ └── api-authorization/
│ │ │ │ ├── AuthContext.jsx
│ │ │ │ ├── LoginPage.jsx
│ │ │ │ ├── ProtectedRoute.jsx
│ │ │ │ └── RegisterPage.jsx
│ │ │ ├── main.jsx
│ │ │ ├── styles.scss
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── DependencyInjection.cs
│ ├── Endpoints/
│ │ ├── TodoItems.cs
│ │ ├── TodoLists.cs
│ │ ├── Users.cs
│ │ └── WeatherForecasts.cs
│ ├── GlobalUsings.cs
│ ├── Infrastructure/
│ │ ├── ApiExceptionOperationTransformer.cs
│ │ ├── BearerSecuritySchemeTransformer.cs
│ │ ├── EndpointRouteBuilderExtensions.cs
│ │ ├── IEndpointGroup.cs
│ │ ├── IdentityApiOperationTransformer.cs
│ │ ├── MethodInfoExtensions.cs
│ │ ├── ProblemDetailsExceptionHandler.cs
│ │ └── WebApplicationExtensions.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Services/
│ │ └── CurrentUser.cs
│ ├── Web-webapi.http
│ ├── Web.csproj
│ ├── Web.http
│ ├── appsettings.PostgreSQL.json
│ ├── appsettings.SQLServer.json
│ ├── appsettings.SQLite.json
│ └── appsettings.json
├── templates/
│ └── ca-use-case/
│ ├── .template.config/
│ │ ├── dotnetcli.host.json
│ │ └── template.json
│ └── FeatureName/
│ ├── Commands/
│ │ └── CleanArchitectureUseCase/
│ │ └── CleanArchitectureUseCase.cs
│ └── Queries/
│ └── CleanArchitectureUseCase/
│ └── CleanArchitectureUseCase.cs
└── tests/
├── Application.FunctionalTests/
│ ├── Application.FunctionalTests.csproj
│ ├── FunctionalTestSetup.cs
│ ├── GlobalUsings.cs
│ ├── Infrastructure/
│ │ ├── DatabaseResetter.cs
│ │ ├── TestApp.cs
│ │ ├── TestBase.cs
│ │ └── WebApiFactory.cs
│ ├── TodoItems/
│ │ └── Commands/
│ │ ├── CreateTodoItemTests.cs
│ │ ├── DeleteTodoItemTests.cs
│ │ ├── UpdateTodoItemDetailTests.cs
│ │ └── UpdateTodoItemTests.cs
│ └── TodoLists/
│ ├── Commands/
│ │ ├── CreateTodoListTests.cs
│ │ ├── DeleteTodoListTests.cs
│ │ ├── PurgeTodoListsTests.cs
│ │ └── UpdateTodoListTests.cs
│ └── Queries/
│ └── GetTodosTests.cs
├── Application.UnitTests/
│ ├── Application.UnitTests.csproj
│ └── Common/
│ ├── Behaviours/
│ │ └── RequestLoggerTests.cs
│ ├── Exceptions/
│ │ └── ValidationExceptionTests.cs
│ ├── Mappings/
│ │ └── MappingTests.cs
│ └── Models/
│ └── PaginatedListTests.cs
├── Domain.UnitTests/
│ ├── Domain.UnitTests.csproj
│ └── ValueObjects/
│ └── ColourTests.cs
├── Infrastructure.IntegrationTests/
│ ├── GlobalUsings.cs
│ └── Infrastructure.IntegrationTests.csproj
├── TestAppHost/
│ ├── Program.cs
│ └── TestAppHost.csproj
└── Web.AcceptanceTests/
├── AspireSetup.cs
├── Features/
│ ├── Counter.feature
│ ├── Home.feature
│ ├── Login.feature
│ └── Weather.feature
├── GlobalUsings.cs
├── Pages/
│ ├── BasePage.cs
│ ├── CounterPage.cs
│ ├── HomePage.cs
│ ├── LoginPage.cs
│ └── WeatherPage.cs
├── PlaywrightSetup.cs
├── StepDefinitions/
│ ├── CounterStepDefinitions.cs
│ ├── HomeStepDefinitions.cs
│ ├── LoginStepDefinitions.cs
│ └── WeatherStepDefinitions.cs
└── Web.AcceptanceTests.csproj
SYMBOL INDEX (477 symbols across 143 files)
FILE: src/Application/Common/Behaviours/AuthorizationBehaviour.cs
class AuthorizationBehaviour (line 8) | public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBeha...
method AuthorizationBehaviour (line 14) | public AuthorizationBehaviour(
method Handle (line 22) | public async Task<TResponse> Handle(TRequest request, RequestHandlerDe...
FILE: src/Application/Common/Behaviours/LoggingBehaviour.cs
class LoggingBehaviour (line 7) | public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest>
method LoggingBehaviour (line 14) | public LoggingBehaviour(ILogger<TRequest> logger, IUser user, IIdentit...
method Process (line 21) | public async Task Process(TRequest request, CancellationToken cancella...
FILE: src/Application/Common/Behaviours/PerformanceBehaviour.cs
class PerformanceBehaviour (line 7) | public class PerformanceBehaviour<TRequest, TResponse> : IPipelineBehavi...
method PerformanceBehaviour (line 15) | public PerformanceBehaviour(
method Handle (line 27) | public async Task<TResponse> Handle(TRequest request, RequestHandlerDe...
FILE: src/Application/Common/Behaviours/UnhandledExceptionBehaviour.cs
class UnhandledExceptionBehaviour (line 5) | public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelin...
method UnhandledExceptionBehaviour (line 10) | public UnhandledExceptionBehaviour(ILogger<TRequest> logger)
method Handle (line 15) | public async Task<TResponse> Handle(TRequest request, RequestHandlerDe...
FILE: src/Application/Common/Behaviours/ValidationBehaviour.cs
class ValidationBehaviour (line 5) | public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavio...
method ValidationBehaviour (line 10) | public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
method Handle (line 15) | public async Task<TResponse> Handle(TRequest request, RequestHandlerDe...
FILE: src/Application/Common/Exceptions/ForbiddenAccessException.cs
class ForbiddenAccessException (line 3) | public class ForbiddenAccessException : Exception
method ForbiddenAccessException (line 5) | public ForbiddenAccessException() : base() { }
FILE: src/Application/Common/Exceptions/ValidationException.cs
class ValidationException (line 5) | public class ValidationException : Exception
method ValidationException (line 7) | public ValidationException()
method ValidationException (line 13) | public ValidationException(IEnumerable<ValidationFailure> failures)
FILE: src/Application/Common/Interfaces/IApplicationDbContext.cs
type IApplicationDbContext (line 5) | public interface IApplicationDbContext
method SaveChangesAsync (line 11) | Task<int> SaveChangesAsync(CancellationToken cancellationToken);
FILE: src/Application/Common/Interfaces/IIdentityService.cs
type IIdentityService (line 5) | public interface IIdentityService
method GetUserNameAsync (line 7) | Task<string?> GetUserNameAsync(string userId);
method IsInRoleAsync (line 9) | Task<bool> IsInRoleAsync(string userId, string role);
method AuthorizeAsync (line 11) | Task<bool> AuthorizeAsync(string userId, string policyName);
method CreateUserAsync (line 13) | Task<(Result Result, string UserId)> CreateUserAsync(string userName, ...
method DeleteUserAsync (line 15) | Task<Result> DeleteUserAsync(string userId);
FILE: src/Application/Common/Interfaces/IUser.cs
type IUser (line 3) | public interface IUser
FILE: src/Application/Common/Mappings/MappingExtensions.cs
class MappingExtensions (line 5) | public static class MappingExtensions
method PaginatedListAsync (line 7) | public static Task<PaginatedList<TDestination>> PaginatedListAsync<TDe...
method ProjectToListAsync (line 10) | public static Task<List<TDestination>> ProjectToListAsync<TDestination...
FILE: src/Application/Common/Models/LookupDto.cs
class LookupDto (line 5) | public class LookupDto
class Mapping (line 11) | private class Mapping : Profile
method Mapping (line 13) | public Mapping()
FILE: src/Application/Common/Models/PaginatedList.cs
class PaginatedList (line 3) | public class PaginatedList<T>
method PaginatedList (line 10) | public PaginatedList(IReadOnlyCollection<T> items, int count, int page...
method CreateAsync (line 22) | public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> s...
FILE: src/Application/Common/Models/Result.cs
class Result (line 3) | public class Result
method Result (line 5) | internal Result(bool succeeded, IEnumerable<string> errors)
method Success (line 15) | public static Result Success()
method Failure (line 20) | public static Result Failure(IEnumerable<string> errors)
FILE: src/Application/Common/Security/AuthorizeAttribute.cs
class AuthorizeAttribute (line 6) | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited ...
method AuthorizeAttribute (line 12) | public AuthorizeAttribute() { }
FILE: src/Application/DependencyInjection.cs
class DependencyInjection (line 7) | public static class DependencyInjection
method AddApplicationServices (line 9) | public static void AddApplicationServices(this IHostApplicationBuilder...
FILE: src/Application/TodoItems/Commands/CreateTodoItem/CreateTodoItem.cs
type CreateTodoItemCommand (line 7) | public record CreateTodoItemCommand : IRequest<int>
class CreateTodoItemCommandHandler (line 14) | public class CreateTodoItemCommandHandler : IRequestHandler<CreateTodoIt...
method CreateTodoItemCommandHandler (line 18) | public CreateTodoItemCommandHandler(IApplicationDbContext context)
method Handle (line 23) | public async Task<int> Handle(CreateTodoItemCommand request, Cancellat...
FILE: src/Application/TodoItems/Commands/CreateTodoItem/CreateTodoItemCommandValidator.cs
class CreateTodoItemCommandValidator (line 3) | public class CreateTodoItemCommandValidator : AbstractValidator<CreateTo...
method CreateTodoItemCommandValidator (line 5) | public CreateTodoItemCommandValidator()
FILE: src/Application/TodoItems/Commands/DeleteTodoItem/DeleteTodoItem.cs
type DeleteTodoItemCommand (line 6) | public record DeleteTodoItemCommand(int Id) : IRequest;
class DeleteTodoItemCommandHandler (line 8) | public class DeleteTodoItemCommandHandler : IRequestHandler<DeleteTodoIt...
method DeleteTodoItemCommandHandler (line 12) | public DeleteTodoItemCommandHandler(IApplicationDbContext context)
method Handle (line 17) | public async Task Handle(DeleteTodoItemCommand request, CancellationTo...
FILE: src/Application/TodoItems/Commands/UpdateTodoItem/UpdateTodoItem.cs
type UpdateTodoItemCommand (line 5) | public record UpdateTodoItemCommand : IRequest
class UpdateTodoItemCommandHandler (line 14) | public class UpdateTodoItemCommandHandler : IRequestHandler<UpdateTodoIt...
method UpdateTodoItemCommandHandler (line 18) | public UpdateTodoItemCommandHandler(IApplicationDbContext context)
method Handle (line 23) | public async Task Handle(UpdateTodoItemCommand request, CancellationTo...
FILE: src/Application/TodoItems/Commands/UpdateTodoItem/UpdateTodoItemCommandValidator.cs
class UpdateTodoItemCommandValidator (line 3) | public class UpdateTodoItemCommandValidator : AbstractValidator<UpdateTo...
method UpdateTodoItemCommandValidator (line 5) | public UpdateTodoItemCommandValidator()
FILE: src/Application/TodoItems/Commands/UpdateTodoItemDetail/UpdateTodoItemDetail.cs
type UpdateTodoItemDetailCommand (line 6) | public record UpdateTodoItemDetailCommand : IRequest
class UpdateTodoItemDetailCommandHandler (line 17) | public class UpdateTodoItemDetailCommandHandler : IRequestHandler<Update...
method UpdateTodoItemDetailCommandHandler (line 21) | public UpdateTodoItemDetailCommandHandler(IApplicationDbContext context)
method Handle (line 26) | public async Task Handle(UpdateTodoItemDetailCommand request, Cancella...
FILE: src/Application/TodoItems/EventHandlers/LogTodoItemCompleted.cs
class LogTodoItemCompleted (line 6) | public class LogTodoItemCompleted : INotificationHandler<TodoItemComplet...
method LogTodoItemCompleted (line 10) | public LogTodoItemCompleted(ILogger<LogTodoItemCompleted> logger)
method Handle (line 15) | public Task Handle(TodoItemCompletedEvent notification, CancellationTo...
FILE: src/Application/TodoItems/EventHandlers/LogTodoItemCreated.cs
class LogTodoItemCreated (line 6) | public class LogTodoItemCreated : INotificationHandler<TodoItemCreatedEv...
method LogTodoItemCreated (line 10) | public LogTodoItemCreated(ILogger<LogTodoItemCreated> logger)
method Handle (line 15) | public Task Handle(TodoItemCreatedEvent notification, CancellationToke...
FILE: src/Application/TodoItems/Queries/GetTodoItemsWithPagination/GetTodoItemsWithPagination.cs
type GetTodoItemsWithPaginationQuery (line 7) | public record GetTodoItemsWithPaginationQuery : IRequest<PaginatedList<T...
class GetTodoItemsWithPaginationQueryHandler (line 14) | public class GetTodoItemsWithPaginationQueryHandler : IRequestHandler<Ge...
method GetTodoItemsWithPaginationQueryHandler (line 19) | public GetTodoItemsWithPaginationQueryHandler(IApplicationDbContext co...
method Handle (line 25) | public async Task<PaginatedList<TodoItemBriefDto>> Handle(GetTodoItems...
FILE: src/Application/TodoItems/Queries/GetTodoItemsWithPagination/GetTodoItemsWithPaginationQueryValidator.cs
class GetTodoItemsWithPaginationQueryValidator (line 3) | public class GetTodoItemsWithPaginationQueryValidator : AbstractValidato...
method GetTodoItemsWithPaginationQueryValidator (line 5) | public GetTodoItemsWithPaginationQueryValidator()
FILE: src/Application/TodoItems/Queries/GetTodoItemsWithPagination/TodoItemBriefDto.cs
class TodoItemBriefDto (line 5) | public class TodoItemBriefDto
class Mapping (line 15) | private class Mapping : Profile
method Mapping (line 17) | public Mapping()
FILE: src/Application/TodoLists/Commands/CreateTodoList/CreateTodoList.cs
type CreateTodoListCommand (line 6) | public record CreateTodoListCommand : IRequest<int>
class CreateTodoListCommandHandler (line 11) | public class CreateTodoListCommandHandler : IRequestHandler<CreateTodoLi...
method CreateTodoListCommandHandler (line 15) | public CreateTodoListCommandHandler(IApplicationDbContext context)
method Handle (line 20) | public async Task<int> Handle(CreateTodoListCommand request, Cancellat...
FILE: src/Application/TodoLists/Commands/CreateTodoList/CreateTodoListCommandValidator.cs
class CreateTodoListCommandValidator (line 5) | public class CreateTodoListCommandValidator : AbstractValidator<CreateTo...
method CreateTodoListCommandValidator (line 9) | public CreateTodoListCommandValidator(IApplicationDbContext context)
method BeUniqueTitle (line 21) | public async Task<bool> BeUniqueTitle(string title, CancellationToken ...
FILE: src/Application/TodoLists/Commands/DeleteTodoList/DeleteTodoList.cs
type DeleteTodoListCommand (line 5) | public record DeleteTodoListCommand(int Id) : IRequest;
class DeleteTodoListCommandHandler (line 7) | public class DeleteTodoListCommandHandler : IRequestHandler<DeleteTodoLi...
method DeleteTodoListCommandHandler (line 11) | public DeleteTodoListCommandHandler(IApplicationDbContext context)
method Handle (line 16) | public async Task Handle(DeleteTodoListCommand request, CancellationTo...
FILE: src/Application/TodoLists/Commands/PurgeTodoLists/PurgeTodoLists.cs
type PurgeTodoListsCommand (line 7) | [Authorize(Roles = Roles.Administrator)]
class PurgeTodoListsCommandHandler (line 11) | public class PurgeTodoListsCommandHandler : IRequestHandler<PurgeTodoLis...
method PurgeTodoListsCommandHandler (line 15) | public PurgeTodoListsCommandHandler(IApplicationDbContext context)
method Handle (line 20) | public async Task Handle(PurgeTodoListsCommand request, CancellationTo...
FILE: src/Application/TodoLists/Commands/UpdateTodoList/UpdateTodoList.cs
type UpdateTodoListCommand (line 5) | public record UpdateTodoListCommand : IRequest
class UpdateTodoListCommandHandler (line 12) | public class UpdateTodoListCommandHandler : IRequestHandler<UpdateTodoLi...
method UpdateTodoListCommandHandler (line 16) | public UpdateTodoListCommandHandler(IApplicationDbContext context)
method Handle (line 21) | public async Task Handle(UpdateTodoListCommand request, CancellationTo...
FILE: src/Application/TodoLists/Commands/UpdateTodoList/UpdateTodoListCommandValidator.cs
class UpdateTodoListCommandValidator (line 5) | public class UpdateTodoListCommandValidator : AbstractValidator<UpdateTo...
method UpdateTodoListCommandValidator (line 9) | public UpdateTodoListCommandValidator(IApplicationDbContext context)
method BeUniqueTitle (line 21) | public async Task<bool> BeUniqueTitle(UpdateTodoListCommand model, str...
FILE: src/Application/TodoLists/Queries/GetTodos/GetTodos.cs
type GetTodosQuery (line 8) | [Authorize]
class GetTodosQueryHandler (line 11) | public class GetTodosQueryHandler : IRequestHandler<GetTodosQuery, TodosVm>
method GetTodosQueryHandler (line 16) | public GetTodosQueryHandler(IApplicationDbContext context, IMapper map...
method Handle (line 22) | public async Task<TodosVm> Handle(GetTodosQuery request, CancellationT...
FILE: src/Application/TodoLists/Queries/GetTodos/TodoItemDto.cs
class TodoItemDto (line 5) | public class TodoItemDto
class Mapping (line 19) | private class Mapping : Profile
method Mapping (line 21) | public Mapping()
FILE: src/Application/TodoLists/Queries/GetTodos/TodoListDto.cs
class TodoListDto (line 5) | public class TodoListDto
method TodoListDto (line 7) | public TodoListDto()
class Mapping (line 20) | private class Mapping : Profile
method Mapping (line 22) | public Mapping()
FILE: src/Application/TodoLists/Queries/GetTodos/TodosVm.cs
class TodosVm (line 5) | public class TodosVm
FILE: src/Application/WeatherForecasts/Queries/GetWeatherForecasts/GetWeatherForecastsQuery.cs
type GetWeatherForecastsQuery (line 3) | public record GetWeatherForecastsQuery : IRequest<IEnumerable<WeatherFor...
class GetWeatherForecastsQueryHandler (line 5) | public class GetWeatherForecastsQueryHandler : IRequestHandler<GetWeathe...
method Handle (line 13) | public async Task<IEnumerable<WeatherForecast>> Handle(GetWeatherForec...
FILE: src/Application/WeatherForecasts/Queries/GetWeatherForecasts/WeatherForecast.cs
class WeatherForecast (line 3) | public class WeatherForecast
FILE: src/Domain/Common/BaseAuditableEntity.cs
class BaseAuditableEntity (line 3) | public abstract class BaseAuditableEntity : BaseEntity
FILE: src/Domain/Common/BaseEntity.cs
class BaseEntity (line 5) | public abstract class BaseEntity
method AddDomainEvent (line 16) | public void AddDomainEvent(BaseEvent domainEvent)
method RemoveDomainEvent (line 21) | public void RemoveDomainEvent(BaseEvent domainEvent)
method ClearDomainEvents (line 26) | public void ClearDomainEvents()
FILE: src/Domain/Common/BaseEvent.cs
class BaseEvent (line 5) | public abstract class BaseEvent : INotification
FILE: src/Domain/Common/ValueObject.cs
class ValueObject (line 4) | public abstract class ValueObject
method EqualOperator (line 6) | protected static bool EqualOperator(ValueObject left, ValueObject right)
method NotEqualOperator (line 16) | protected static bool NotEqualOperator(ValueObject left, ValueObject r...
method GetEqualityComponents (line 21) | protected abstract IEnumerable<object> GetEqualityComponents();
method Equals (line 23) | public override bool Equals(object? obj)
method GetHashCode (line 34) | public override int GetHashCode()
FILE: src/Domain/Constants/Policies.cs
class Policies (line 3) | public abstract class Policies
FILE: src/Domain/Constants/Roles.cs
class Roles (line 3) | public abstract class Roles
FILE: src/Domain/Entities/TodoItem.cs
class TodoItem (line 3) | public class TodoItem : BaseAuditableEntity
FILE: src/Domain/Entities/TodoList.cs
class TodoList (line 3) | public class TodoList : BaseAuditableEntity
FILE: src/Domain/Enums/PriorityLevel.cs
type PriorityLevel (line 3) | public enum PriorityLevel
FILE: src/Domain/Events/TodoItemCompletedEvent.cs
class TodoItemCompletedEvent (line 3) | public class TodoItemCompletedEvent : BaseEvent
method TodoItemCompletedEvent (line 5) | public TodoItemCompletedEvent(TodoItem item)
FILE: src/Domain/Events/TodoItemCreatedEvent.cs
class TodoItemCreatedEvent (line 3) | public class TodoItemCreatedEvent : BaseEvent
method TodoItemCreatedEvent (line 5) | public TodoItemCreatedEvent(TodoItem item)
FILE: src/Domain/Events/TodoItemDeletedEvent.cs
class TodoItemDeletedEvent (line 3) | public class TodoItemDeletedEvent : BaseEvent
method TodoItemDeletedEvent (line 5) | public TodoItemDeletedEvent(TodoItem item)
FILE: src/Domain/Exceptions/UnsupportedColourException.cs
class UnsupportedColourException (line 3) | public class UnsupportedColourException : Exception
method UnsupportedColourException (line 5) | public UnsupportedColourException(string code)
FILE: src/Domain/ValueObjects/Colour.cs
class Colour (line 3) | public class Colour(string code) : ValueObject
method From (line 5) | public static Colour From(string code)
method ToString (line 45) | public override string ToString()
method GetEqualityComponents (line 65) | protected override IEnumerable<object> GetEqualityComponents()
FILE: src/Infrastructure/Data/ApplicationDbContext.cs
class ApplicationDbContext (line 10) | public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, ...
method ApplicationDbContext (line 12) | public ApplicationDbContext(DbContextOptions<ApplicationDbContext> opt...
method OnModelCreating (line 18) | protected override void OnModelCreating(ModelBuilder builder)
FILE: src/Infrastructure/Data/ApplicationDbContextInitialiser.cs
class InitialiserExtensions (line 11) | public static class InitialiserExtensions
method InitialiseDatabaseAsync (line 13) | public static async Task InitialiseDatabaseAsync(this WebApplication app)
class ApplicationDbContextInitialiser (line 24) | public class ApplicationDbContextInitialiser
method ApplicationDbContextInitialiser (line 31) | public ApplicationDbContextInitialiser(ILogger<ApplicationDbContextIni...
method InitialiseAsync (line 39) | public async Task InitialiseAsync()
method SeedAsync (line 54) | public async Task SeedAsync()
method TrySeedAsync (line 67) | public async Task TrySeedAsync()
FILE: src/Infrastructure/Data/Configurations/TodoItemConfiguration.cs
class TodoItemConfiguration (line 7) | public class TodoItemConfiguration : IEntityTypeConfiguration<TodoItem>
method Configure (line 9) | public void Configure(EntityTypeBuilder<TodoItem> builder)
FILE: src/Infrastructure/Data/Configurations/TodoListConfiguration.cs
class TodoListConfiguration (line 7) | public class TodoListConfiguration : IEntityTypeConfiguration<TodoList>
method Configure (line 9) | public void Configure(EntityTypeBuilder<TodoList> builder)
FILE: src/Infrastructure/Data/Interceptors/AuditableEntityInterceptor.cs
class AuditableEntityInterceptor (line 9) | public class AuditableEntityInterceptor : SaveChangesInterceptor
method AuditableEntityInterceptor (line 14) | public AuditableEntityInterceptor(
method SavingChanges (line 22) | public override InterceptionResult<int> SavingChanges(DbContextEventDa...
method SavingChangesAsync (line 29) | public override ValueTask<InterceptionResult<int>> SavingChangesAsync(...
method UpdateEntities (line 36) | public void UpdateEntities(DbContext? context)
class Extensions (line 57) | public static class Extensions
method HasChangedOwnedEntities (line 59) | public static bool HasChangedOwnedEntities(this EntityEntry entry) =>
FILE: src/Infrastructure/Data/Interceptors/DispatchDomainEventsInterceptor.cs
class DispatchDomainEventsInterceptor (line 8) | public class DispatchDomainEventsInterceptor : SaveChangesInterceptor
method DispatchDomainEventsInterceptor (line 12) | public DispatchDomainEventsInterceptor(IMediator mediator)
method SavingChanges (line 17) | public override InterceptionResult<int> SavingChanges(DbContextEventDa...
method SavingChangesAsync (line 25) | public override async ValueTask<InterceptionResult<int>> SavingChanges...
method DispatchDomainEvents (line 32) | public async Task DispatchDomainEvents(DbContext? context)
FILE: src/Infrastructure/DependencyInjection.cs
class DependencyInjection (line 14) | public static class DependencyInjection
method AddInfrastructureServices (line 16) | public static void AddInfrastructureServices(this IHostApplicationBuil...
FILE: src/Infrastructure/Identity/ApplicationUser.cs
class ApplicationUser (line 5) | public class ApplicationUser : IdentityUser
FILE: src/Infrastructure/Identity/IdentityResultExtensions.cs
class IdentityResultExtensions (line 6) | public static class IdentityResultExtensions
method ToApplicationResult (line 8) | public static Result ToApplicationResult(this IdentityResult result)
FILE: src/Infrastructure/Identity/IdentityService.cs
class IdentityService (line 9) | public class IdentityService : IIdentityService
method IdentityService (line 15) | public IdentityService(
method GetUserNameAsync (line 25) | public async Task<string?> GetUserNameAsync(string userId)
method CreateUserAsync (line 32) | public async Task<(Result Result, string UserId)> CreateUserAsync(stri...
method IsInRoleAsync (line 45) | public async Task<bool> IsInRoleAsync(string userId, string role)
method AuthorizeAsync (line 52) | public async Task<bool> AuthorizeAsync(string userId, string policyName)
method DeleteUserAsync (line 68) | public async Task<Result> DeleteUserAsync(string userId)
method DeleteUserAsync (line 75) | public async Task<Result> DeleteUserAsync(ApplicationUser user)
FILE: src/ServiceDefaults/Extensions.cs
class Extensions (line 16) | public static class Extensions
method AddServiceDefaults (line 18) | public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder buil...
method ConfigureOpenTelemetry (line 44) | public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder ...
method AddOpenTelemetryExporters (line 73) | private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuil...
method AddDefaultHealthChecks (line 92) | public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder ...
method MapDefaultEndpoints (line 101) | public static WebApplication MapDefaultEndpoints(this WebApplication app)
FILE: src/Shared/Services.cs
class Services (line 3) | public static class Services
FILE: src/Web/ClientApp-React/src/App.jsx
class App (line 8) | class App extends Component {
method render (line 11) | render() {
FILE: src/Web/ClientApp-React/src/components/Counter.jsx
function Counter (line 3) | function Counter() {
FILE: src/Web/ClientApp-React/src/components/Home.jsx
function Home (line 1) | function Home() {
FILE: src/Web/ClientApp-React/src/components/Layout.jsx
function Layout (line 3) | function Layout({ children }) {
FILE: src/Web/ClientApp-React/src/components/NavMenu.jsx
function AuthLinks (line 5) | function AuthLinks() {
function NavMenu (line 26) | function NavMenu() {
FILE: src/Web/ClientApp-React/src/components/ThemeContext.jsx
constant STORAGE_KEY (line 3) | const STORAGE_KEY = 'picoColorScheme';
function useTheme (line 7) | function useTheme() {
function ThemeProvider (line 11) | function ThemeProvider({ children }) {
FILE: src/Web/ClientApp-React/src/components/ThemeToggle.jsx
function ThemeToggle (line 12) | function ThemeToggle() {
FILE: src/Web/ClientApp-React/src/components/Todo.jsx
function Tasks (line 8) | function Tasks() {
FILE: src/Web/ClientApp-React/src/components/Weather.jsx
function Weather (line 4) | function Weather() {
FILE: src/Web/ClientApp-React/src/components/api-authorization/AuthContext.jsx
function AuthProvider (line 8) | function AuthProvider({ children }) {
FILE: src/Web/ClientApp-React/src/components/api-authorization/LoginPage.jsx
function LoginPage (line 5) | function LoginPage() {
FILE: src/Web/ClientApp-React/src/components/api-authorization/ProtectedRoute.jsx
function ProtectedRoute (line 4) | function ProtectedRoute({ children }) {
FILE: src/Web/ClientApp-React/src/components/api-authorization/RegisterPage.jsx
constant MIN_PASSWORD_LENGTH (line 5) | const MIN_PASSWORD_LENGTH = 6;
function validateEmail (line 7) | function validateEmail(value) {
function RegisterPage (line 11) | function RegisterPage() {
FILE: src/Web/ClientApp/proxy.conf.js
constant PROXY_CONFIG (line 7) | const PROXY_CONFIG = [
FILE: src/Web/ClientApp/src/api-authorization/auth.guard.ts
class AuthGuard (line 10) | class AuthGuard implements CanActivate {
method constructor (line 11) | constructor(private authService: AuthService, private router: Router) {}
method canActivate (line 13) | canActivate(_route: ActivatedRouteSnapshot, state: RouterStateSnapshot...
FILE: src/Web/ClientApp/src/api-authorization/auth.service.ts
class AuthService (line 9) | class AuthService {
method constructor (line 13) | constructor(private usersClient: UsersClient) {}
method initialize (line 15) | initialize(): Observable<boolean> {
method login (line 23) | login(email: string, password: string): Observable<void> {
method register (line 30) | register(email: string, password: string): Observable<void> {
method logout (line 34) | logout(): Observable<void> {
FILE: src/Web/ClientApp/src/api-authorization/authorize.interceptor.ts
class AuthorizeInterceptor (line 10) | class AuthorizeInterceptor implements HttpInterceptor {
method constructor (line 11) | constructor(private router: Router) {}
method intercept (line 13) | intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEv...
FILE: src/Web/ClientApp/src/api-authorization/login/login.component.ts
class LoginComponent (line 11) | class LoginComponent {
method constructor (line 16) | constructor(
method login (line 23) | async login() {
FILE: src/Web/ClientApp/src/api-authorization/register/register.component.ts
constant MIN_PASSWORD_LENGTH (line 6) | const MIN_PASSWORD_LENGTH = 6;
class RegisterComponent (line 13) | class RegisterComponent {
method emailValid (line 22) | get emailValid() { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email...
method passwordValid (line 23) | get passwordValid() { return this.password.length >= MIN_PASSWORD_LENG...
method constructor (line 25) | constructor(private authService: AuthService, private router: Router, ...
method register (line 27) | async register() {
FILE: src/Web/ClientApp/src/app/app.component.ts
class AppComponent (line 8) | class AppComponent {
FILE: src/Web/ClientApp/src/app/app.module.ts
function getApiBaseUrl (line 22) | function getApiBaseUrl(): string {
class AppModule (line 61) | class AppModule { }
FILE: src/Web/ClientApp/src/app/app.server.module.ts
class AppServerModule (line 10) | class AppServerModule { }
FILE: src/Web/ClientApp/src/app/counter/counter.component.ts
class CounterComponent (line 8) | class CounterComponent {
method incrementCounter (line 11) | public incrementCounter() {
FILE: src/Web/ClientApp/src/app/fetch-data/fetch-data.component.ts
class FetchDataComponent (line 9) | class FetchDataComponent {
method constructor (line 12) | constructor(private client: WeatherForecastsClient, private cdr: Chang...
FILE: src/Web/ClientApp/src/app/home/home.component.ts
class HomeComponent (line 8) | class HomeComponent {
FILE: src/Web/ClientApp/src/app/nav-menu/nav-menu.component.ts
class NavMenuComponent (line 11) | class NavMenuComponent {
method constructor (line 14) | constructor(private authService: AuthService, private router: Router) {}
method logout (line 16) | logout(event: Event): void {
FILE: src/Web/ClientApp/src/app/theme-toggle/theme-toggle.component.ts
class ThemeToggleComponent (line 9) | class ThemeToggleComponent {
method constructor (line 10) | constructor(public themeService: ThemeService) {}
method cycle (line 28) | cycle(): void {
FILE: src/Web/ClientApp/src/app/theme.service.ts
type Theme (line 3) | type Theme = 'auto' | 'light' | 'dark';
class ThemeService (line 6) | class ThemeService {
method constructor (line 11) | constructor() {
method setTheme (line 18) | setTheme(value: Theme): void {
method applyTheme (line 24) | private applyTheme(theme: Theme): void {
FILE: src/Web/ClientApp/src/app/todo/todo.component.ts
class TasksComponent (line 14) | class TasksComponent implements OnInit {
method constructor (line 34) | constructor(
method ngOnInit (line 41) | ngOnInit(): void {
method remainingItems (line 55) | remainingItems(list: TodoListDto): number {
method showNewListDialog (line 59) | showNewListDialog(): void {
method newListCancelled (line 66) | newListCancelled(): void {
method addList (line 72) | addList(): void {
method showListOptionsDialog (line 98) | showListOptionsDialog(): void {
method closeListOptionsDialog (line 106) | closeListOptionsDialog(): void {
method updateListOptions (line 111) | updateListOptions(): void {
method confirmDeleteList (line 123) | confirmDeleteList(): void {
method closeDeleteListDialog (line 128) | closeDeleteListDialog(): void {
method deleteListConfirmed (line 132) | deleteListConfirmed(): void {
method showItemDetailsDialog (line 146) | showItemDetailsDialog(item: TodoItemDto): void {
method closeItemDetailsDialog (line 152) | closeItemDetailsDialog(): void {
method updateItemDetails (line 158) | updateItemDetails(): void {
method startAddingItem (line 185) | startAddingItem(): void {
method cancelNewItem (line 190) | cancelNewItem(): void {
method commitNewItem (line 195) | commitNewItem(): void {
method editItem (line 215) | editItem(item: TodoItemDto, inputId: string): void {
method cancelEdit (line 221) | cancelEdit(): void {
method updateItem (line 228) | updateItem(item: TodoItemDto): void {
method deleteItem (line 257) | deleteItem(item: TodoItemDto): void {
FILE: src/Web/ClientApp/src/app/weather/weather.component.ts
class WeatherComponent (line 9) | class WeatherComponent {
method constructor (line 14) | constructor(private client: WeatherForecastsClient, private cdr: Chang...
FILE: src/Web/ClientApp/src/main.ts
function getBaseUrl (line 7) | function getBaseUrl() {
FILE: src/Web/DependencyInjection.cs
class DependencyInjection (line 9) | public static class DependencyInjection
method AddWebServices (line 11) | public static void AddWebServices(this IHostApplicationBuilder builder)
method AddKeyVaultIfConfigured (line 39) | public static void AddKeyVaultIfConfigured(this IHostApplicationBuilde...
FILE: src/Web/Endpoints/TodoItems.cs
class TodoItems (line 11) | public class TodoItems : IEndpointGroup
method Map (line 13) | public static void Map(RouteGroupBuilder groupBuilder)
method GetTodoItemsWithPagination (line 24) | [EndpointSummary("Get Todo Items with Pagination")]
method CreateTodoItem (line 35) | [EndpointSummary("Create a new Todo Item")]
method UpdateTodoItem (line 44) | [EndpointSummary("Update a Todo Item")]
method UpdateTodoItemDetail (line 56) | [EndpointSummary("Update Todo Item Details")]
method DeleteTodoItem (line 67) | [EndpointSummary("Delete a Todo Item")]
FILE: src/Web/Endpoints/TodoLists.cs
class TodoLists (line 9) | public class TodoLists : IEndpointGroup
method Map (line 11) | public static void Map(RouteGroupBuilder groupBuilder)
method GetTodoLists (line 21) | [EndpointSummary("Get all Todo Lists")]
method CreateTodoList (line 30) | [EndpointSummary("Create a new Todo List")]
method UpdateTodoList (line 39) | [EndpointSummary("Update a Todo List")]
method DeleteTodoList (line 50) | [EndpointSummary("Delete a Todo List")]
FILE: src/Web/Endpoints/Users.cs
class Users (line 8) | public class Users : IEndpointGroup
method Map (line 10) | public static void Map(RouteGroupBuilder groupBuilder)
method Logout (line 17) | [EndpointSummary("Log out")]
FILE: src/Web/Endpoints/WeatherForecasts.cs
class WeatherForecasts (line 6) | public class WeatherForecasts : IEndpointGroup
method Map (line 8) | public static void Map(RouteGroupBuilder groupBuilder)
method GetWeatherForecasts (line 15) | [EndpointSummary("Get Weather Forecasts")]
FILE: src/Web/Infrastructure/ApiExceptionOperationTransformer.cs
class ApiExceptionOperationTransformer (line 13) | internal sealed class ApiExceptionOperationTransformer : IOpenApiOperati...
method TransformAsync (line 15) | public Task TransformAsync(OpenApiOperation operation, OpenApiOperatio...
FILE: src/Web/Infrastructure/BearerSecuritySchemeTransformer.cs
class BearerSecuritySchemeTransformer (line 12) | internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSch...
method TransformAsync (line 14) | public async Task TransformAsync(OpenApiDocument document, OpenApiDocu...
FILE: src/Web/Infrastructure/EndpointRouteBuilderExtensions.cs
class EndpointRouteBuilderExtensions (line 17) | public static class EndpointRouteBuilderExtensions
method MapGet (line 20) | public static RouteHandlerBuilder MapGet(this IEndpointRouteBuilder bu...
method MapPost (line 29) | public static RouteHandlerBuilder MapPost(this IEndpointRouteBuilder b...
method MapPut (line 38) | public static RouteHandlerBuilder MapPut(this IEndpointRouteBuilder bu...
method MapPatch (line 47) | public static RouteHandlerBuilder MapPatch(this IEndpointRouteBuilder ...
method MapDelete (line 56) | public static RouteHandlerBuilder MapDelete(this IEndpointRouteBuilder...
FILE: src/Web/Infrastructure/IEndpointGroup.cs
type IEndpointGroup (line 10) | public interface IEndpointGroup
method Map (line 18) | static abstract void Map(RouteGroupBuilder groupBuilder);
FILE: src/Web/Infrastructure/IdentityApiOperationTransformer.cs
class IdentityApiOperationTransformer (line 13) | internal sealed class IdentityApiOperationTransformer : IOpenApiOperatio...
method TransformAsync (line 29) | public Task TransformAsync(OpenApiOperation operation, OpenApiOperatio...
FILE: src/Web/Infrastructure/MethodInfoExtensions.cs
class MethodInfoExtensions (line 5) | public static class MethodInfoExtensions
method IsAnonymous (line 14) | public static bool IsAnonymous(this MethodInfo method) =>
method AnonymousMethod (line 22) | public static void AnonymousMethod(this IGuardClause guardClause, Dele...
FILE: src/Web/Infrastructure/ProblemDetailsExceptionHandler.cs
class ProblemDetailsExceptionHandler (line 13) | public class ProblemDetailsExceptionHandler : IExceptionHandler
method TryHandleAsync (line 15) | public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, E...
FILE: src/Web/Infrastructure/WebApplicationExtensions.cs
class WebApplicationExtensions (line 5) | public static class WebApplicationExtensions
method MapEndpoints (line 12) | public static WebApplication MapEndpoints(this WebApplication app, Ass...
FILE: src/Web/Services/CurrentUser.cs
class CurrentUser (line 7) | public class CurrentUser : IUser
method CurrentUser (line 11) | public CurrentUser(IHttpContextAccessor httpContextAccessor)
FILE: templates/ca-use-case/FeatureName/Commands/CleanArchitectureUseCase/CleanArchitectureUseCase.cs
type CleanArchitectureUseCaseCommand (line 6) | public record CleanArchitectureUseCaseCommand : IRequest<TReturnType>
class CleanArchitectureUseCaseCommandValidator (line 13) | public class CleanArchitectureUseCaseCommandValidator : AbstractValidato...
method CleanArchitectureUseCaseCommandValidator (line 15) | public CleanArchitectureUseCaseCommandValidator()
class CleanArchitectureUseCaseCommandHandler (line 21) | public class CleanArchitectureUseCaseCommandHandler : IRequestHandler<Cl...
method CleanArchitectureUseCaseCommandHandler (line 28) | public CleanArchitectureUseCaseCommandHandler(IApplicationDbContext co...
method Handle (line 34) | public async Task<TReturnType> Handle(CleanArchitectureUseCaseCommand ...
method Handle (line 39) | public async Task Handle(CleanArchitectureUseCaseCommand request, Canc...
FILE: templates/ca-use-case/FeatureName/Queries/CleanArchitectureUseCase/CleanArchitectureUseCase.cs
type CleanArchitectureUseCaseQuery (line 6) | public record CleanArchitectureUseCaseQuery : IRequest<TReturnType>
class CleanArchitectureUseCaseQueryValidator (line 13) | public class CleanArchitectureUseCaseQueryValidator : AbstractValidator<...
method CleanArchitectureUseCaseQueryValidator (line 15) | public CleanArchitectureUseCaseQueryValidator()
class CleanArchitectureUseCaseQueryHandler (line 21) | public class CleanArchitectureUseCaseQueryHandler : IRequestHandler<Clea...
method CleanArchitectureUseCaseQueryHandler (line 28) | public CleanArchitectureUseCaseQueryHandler(IApplicationDbContext cont...
method Handle (line 34) | public async Task<TReturnType> Handle(CleanArchitectureUseCaseQuery re...
method Handle (line 39) | public async Task Handle(CleanArchitectureUseCaseQuery request, Cancel...
FILE: tests/Application.FunctionalTests/FunctionalTestSetup.cs
class FunctionalTestSetup (line 5) | [SetUpFixture]
method OneTimeSetUp (line 14) | [OneTimeSetUp]
method OneTimeTearDown (line 48) | [OneTimeTearDown]
FILE: tests/Application.FunctionalTests/Infrastructure/DatabaseResetter.cs
class DatabaseResetter (line 13) | internal sealed class DatabaseResetter : IAsyncDisposable
method DatabaseResetter (line 18) | private DatabaseResetter(DbConnection connection, Respawner respawner)
method CreateAsync (line 24) | public static async Task<DatabaseResetter> CreateAsync(string connecti...
method ResetAsync (line 40) | public async Task ResetAsync()
method DisposeAsync (line 47) | public async ValueTask DisposeAsync() => await _connection.DisposeAsyn...
FILE: tests/Application.FunctionalTests/Infrastructure/TestApp.cs
class TestApp (line 11) | public static class TestApp
method SendAsync (line 16) | public static async Task<TResponse> SendAsync<TResponse>(IRequest<TRes...
method SendAsync (line 25) | public static async Task SendAsync(IBaseRequest request)
method GetUserId (line 34) | public static string? GetUserId() => _userId;
method GetRoles (line 36) | public static List<string>? GetRoles() => _roles;
method RunAsDefaultUserAsync (line 38) | public static async Task<string> RunAsDefaultUserAsync()
method RunAsAdministratorAsync (line 43) | public static async Task<string> RunAsAdministratorAsync()
method RunAsUserAsync (line 48) | public static async Task<string> RunAsUserAsync(string userName, strin...
method ResetState (line 82) | public static async Task ResetState()
method FindAsync (line 93) | public static async Task<TEntity?> FindAsync<TEntity>(params object[] ...
method AddAsync (line 103) | public static async Task AddAsync<TEntity>(TEntity entity)
method CountAsync (line 115) | public static async Task<int> CountAsync<TEntity>() where TEntity : class
FILE: tests/Application.FunctionalTests/Infrastructure/TestBase.cs
class TestBase (line 3) | public abstract class TestBase
method SetUp (line 5) | [SetUp]
FILE: tests/Application.FunctionalTests/Infrastructure/WebApiFactory.cs
class WebApiFactory (line 10) | public class WebApiFactory(string connectionString) : WebApplicationFact...
method ConfigureWebHost (line 12) | protected override void ConfigureWebHost(IWebHostBuilder builder)
FILE: tests/Application.FunctionalTests/TodoItems/Commands/CreateTodoItemTests.cs
class CreateTodoItemTests (line 8) | public class CreateTodoItemTests : TestBase
method ShouldRequireMinimumFields (line 10) | [Test]
method ShouldCreateTodoItem (line 18) | [Test]
FILE: tests/Application.FunctionalTests/TodoItems/Commands/DeleteTodoItemTests.cs
class DeleteTodoItemTests (line 8) | public class DeleteTodoItemTests : TestBase
method ShouldRequireValidTodoItemId (line 10) | [Test]
method ShouldDeleteTodoItem (line 18) | [Test]
FILE: tests/Application.FunctionalTests/TodoItems/Commands/UpdateTodoItemDetailTests.cs
class UpdateTodoItemDetailTests (line 10) | public class UpdateTodoItemDetailTests : TestBase
method ShouldRequireValidTodoItemId (line 12) | [Test]
method ShouldUpdateTodoItem (line 20) | [Test]
FILE: tests/Application.FunctionalTests/TodoItems/Commands/UpdateTodoItemTests.cs
class UpdateTodoItemTests (line 8) | public class UpdateTodoItemTests : TestBase
method ShouldRequireValidTodoItemId (line 10) | [Test]
method ShouldUpdateTodoItem (line 17) | [Test]
FILE: tests/Application.FunctionalTests/TodoLists/Commands/CreateTodoListTests.cs
class CreateTodoListTests (line 7) | public class CreateTodoListTests : TestBase
method ShouldRequireMinimumFields (line 9) | [Test]
method ShouldRequireUniqueTitle (line 16) | [Test]
method ShouldCreateTodoList (line 32) | [Test]
FILE: tests/Application.FunctionalTests/TodoLists/Commands/DeleteTodoListTests.cs
class DeleteTodoListTests (line 7) | public class DeleteTodoListTests : TestBase
method ShouldRequireValidTodoListId (line 9) | [Test]
method ShouldDeleteTodoList (line 16) | [Test]
FILE: tests/Application.FunctionalTests/TodoLists/Commands/PurgeTodoListsTests.cs
class PurgeTodoListsTests (line 9) | public class PurgeTodoListsTests : TestBase
method ShouldDenyAnonymousUser (line 11) | [Test]
method ShouldDenyNonAdministrator (line 25) | [Test]
method ShouldAllowAdministrator (line 37) | [Test]
method ShouldDeleteAllLists (line 50) | [Test]
FILE: tests/Application.FunctionalTests/TodoLists/Commands/UpdateTodoListTests.cs
class UpdateTodoListTests (line 8) | public class UpdateTodoListTests : TestBase
method ShouldRequireValidTodoListId (line 10) | [Test]
method ShouldRequireUniqueTitle (line 17) | [Test]
method ShouldUpdateTodoList (line 42) | [Test]
FILE: tests/Application.FunctionalTests/TodoLists/Queries/GetTodosTests.cs
class GetTodosTests (line 7) | public class GetTodosTests : TestBase
method ShouldReturnPriorityLevels (line 9) | [Test]
method ShouldReturnAllListsAndItems (line 21) | [Test]
method ShouldDenyAnonymousUser (line 50) | [Test]
FILE: tests/Application.UnitTests/Common/Behaviours/RequestLoggerTests.cs
class RequestLoggerTests (line 10) | public class RequestLoggerTests
method Setup (line 16) | [SetUp]
method ShouldCallGetUserNameAsyncOnceIfAuthenticated (line 24) | [Test]
method ShouldNotCallGetUserNameAsyncOnceIfUnauthenticated (line 36) | [Test]
FILE: tests/Application.UnitTests/Common/Exceptions/ValidationExceptionTests.cs
class ValidationExceptionTests (line 8) | public class ValidationExceptionTests
method DefaultConstructorCreatesAnEmptyErrorDictionary (line 10) | [Test]
method SingleValidationFailureCreatesASingleElementErrorDictionary (line 18) | [Test]
method MulitpleValidationFailureForMultiplePropertiesCreatesAMultipleElementErrorDictionaryEachWithMultipleValues (line 32) | [Test]
FILE: tests/Application.UnitTests/Common/Mappings/MappingTests.cs
class MappingTests (line 13) | public class MappingTests
method OneTimeSetUp (line 19) | [OneTimeSetUp]
method ShouldHaveValidConfiguration (line 32) | [Test]
method ShouldSupportMappingFromSourceToDestination (line 38) | [Test]
method GetInstanceOf (line 51) | private static object GetInstanceOf(Type type)
method OneTimeTearDown (line 61) | [OneTimeTearDown]
FILE: tests/Application.UnitTests/Common/Models/PaginatedListTests.cs
class PaginatedListTests (line 7) | public class PaginatedListTests
method HasPreviousPage_ShouldBeFalse_WhenOnFirstPage (line 9) | [Test]
method HasPreviousPage_ShouldBeTrue_WhenOnSecondPage (line 17) | [Test]
method HasPreviousPage_ShouldBeTrue_WhenOnePageBeyondLastPage (line 25) | [Test]
method HasPreviousPage_ShouldBeFalse_WhenTwoPagesOrMoreBeyondLastPage (line 33) | [Test]
method HasNextPage_ShouldBeFalse_WhenOnLastPage (line 41) | [Test]
method HasNextPage_ShouldBeTrue_WhenNotOnLastPage (line 49) | [Test]
FILE: tests/Domain.UnitTests/ValueObjects/ColourTests.cs
class ColourTests (line 8) | public class ColourTests
method ShouldReturnCorrectColourCode (line 10) | [Test]
method ToStringReturnsCode (line 20) | [Test]
method ShouldPerformImplicitConversionToColourCodeString (line 28) | [Test]
method ShouldPerformExplicitConversionGivenSupportedColourCode (line 36) | [Test]
method ShouldThrowUnsupportedColourExceptionGivenNotSupportedColourCode (line 44) | [Test]
method ShouldBeComparableWithOperators (line 50) | [Test]
FILE: tests/TestAppHost/Program.cs
class Program (line 5) | public class Program
method Main (line 7) | public static void Main(string[] args)
FILE: tests/Web.AcceptanceTests/AspireSetup.cs
class AspireSetup (line 5) | [SetUpFixture]
method OneTimeSetup (line 13) | [OneTimeSetUp]
method OneTimeTearDown (line 55) | [OneTimeTearDown]
FILE: tests/Web.AcceptanceTests/Pages/BasePage.cs
class BasePage (line 3) | public abstract class BasePage(IPage page)
method GotoAsync (line 11) | public Task GotoAsync() => Page.GotoAsync(PagePath);
FILE: tests/Web.AcceptanceTests/Pages/CounterPage.cs
class CounterPage (line 3) | public class CounterPage(IPage page) : BasePage(page)
method AssertHeading (line 7) | public Task AssertHeading(string text)
method AssertCurrentCount (line 10) | public Task AssertCurrentCount(int count)
method ClickIncrement (line 13) | public Task ClickIncrement()
FILE: tests/Web.AcceptanceTests/Pages/HomePage.cs
class HomePage (line 3) | public class HomePage(IPage page) : BasePage(page)
method AssertHeading (line 7) | public Task AssertHeading(string text)
FILE: tests/Web.AcceptanceTests/Pages/LoginPage.cs
class LoginPage (line 3) | public class LoginPage(IPage page) : BasePage(page)
method SetEmail (line 7) | public Task SetEmail(string email)
method SetPassword (line 10) | public Task SetPassword(string password)
method ClickLogin (line 13) | public Task ClickLogin()
method LogoutButtonText (line 16) | public Task<string?> LogoutButtonText()
method AssertErrorVisible (line 19) | public Task AssertErrorVisible()
FILE: tests/Web.AcceptanceTests/Pages/WeatherPage.cs
class WeatherPage (line 3) | public class WeatherPage(IPage page) : BasePage(page)
method AssertHeading (line 7) | public Task AssertHeading(string text)
method AssertTableVisible (line 10) | public Task AssertTableVisible()
method AssertRowCount (line 13) | public Task AssertRowCount(int count)
FILE: tests/Web.AcceptanceTests/PlaywrightSetup.cs
class PlaywrightSetup (line 5) | [SetUpFixture]
method OneTimeSetUp (line 13) | [OneTimeSetUp]
method OneTimeTearDown (line 27) | [OneTimeTearDown]
FILE: tests/Web.AcceptanceTests/StepDefinitions/CounterStepDefinitions.cs
class CounterStepDefinitions (line 3) | [Binding]
method BeforeCounterFeature (line 6) | [BeforeFeature("Counter")]
method AfterCounterFeature (line 15) | [AfterFeature]
method GivenAUserVisitsTheCounterPage (line 22) | [Given("a user visits the counter page")]
method ThenTheCounterHeadingIs (line 25) | [Then("the counter heading is {string}")]
method ThenTheCurrentCountIs (line 28) | [Then("the current count is {int}")]
method WhenTheUserClicksIncrement (line 31) | [When("the user clicks increment")]
FILE: tests/Web.AcceptanceTests/StepDefinitions/HomeStepDefinitions.cs
class HomeStepDefinitions (line 3) | [Binding]
method BeforeHomeFeature (line 6) | [BeforeFeature("Home")]
method AfterHomeFeature (line 15) | [AfterFeature]
method GivenAUserVisitsTheHomePage (line 22) | [Given("a user visits the home page")]
method ThenTheHeadingIsVisible (line 25) | [Then("the heading {string} is visible")]
FILE: tests/Web.AcceptanceTests/StepDefinitions/LoginStepDefinitions.cs
class LoginStepDefinitions (line 3) | [Binding]
method BeforeLoginFeature (line 6) | [BeforeFeature("Login")]
method AfterLoginFeature (line 15) | [AfterFeature]
method GivenALoggedOutUser (line 22) | [Given("a logged out user")]
method TheUserLogsInWithValidCredentials (line 25) | [When("the user logs in with valid credentials")]
method TheyLogInSuccessfully (line 33) | [Then("they log in successfully")]
method TheUserLogsInWithInvalidCredentials (line 42) | [When("the user logs in with invalid credentials")]
method AnErrorIsDisplayed (line 50) | [Then("an error is displayed")]
FILE: tests/Web.AcceptanceTests/StepDefinitions/WeatherStepDefinitions.cs
class WeatherStepDefinitions (line 3) | [Binding]
method BeforeWeatherFeature (line 6) | [BeforeFeature("Weather")]
method AfterWeatherFeature (line 23) | [AfterFeature]
method GivenAnAuthenticatedUserVisitsTheWeatherPage (line 30) | [Given("an authenticated user visits the weather page")]
method ThenTheWeatherForecastHeadingIs (line 33) | [Then("the weather forecast heading is {string}")]
method ThenTheWeatherForecastTableIsDisplayed (line 36) | [Then("the weather forecast table is displayed")]
method ThenWeatherForecastsAreShown (line 39) | [Then("{int} weather forecasts are shown")]
Condensed preview — 311 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (545K chars).
[
{
"path": ".aspire/settings.json",
"chars": 52,
"preview": "{\n \"appHostPath\": \"../src/AppHost/AppHost.csproj\"\n}"
},
{
"path": ".azdo/pipelines/azure-dev.yml",
"chars": 2274,
"preview": "# Run when commits are pushed to mainline branch (main or master)\n# Set this to the mainline branch you are using\ntrigge"
},
{
"path": ".devcontainer/README.md",
"chars": 798,
"preview": "# Dev Container\n\nThis folder contains configuration for running the project inside a **Dev Container** (VS Code Remote C"
},
{
"path": ".devcontainer/devcontainer.json",
"chars": 1058,
"preview": "{\n \"name\": \"Azure Developer CLI\",\n \"image\": \"mcr.microsoft.com/devcontainers/python:3.13-bullseye\",\n \"features\""
},
{
"path": ".editorconfig",
"chars": 17875,
"preview": "root = true\n\n# All files\n[*]\nindent_style = space\n\n# Xml files\n[*.{xml,csproj,props,targets,ruleset,nuspec,resx}]\nindent"
},
{
"path": ".github/FUNDING.yml",
"chars": 838,
"preview": "# These are supported funding model platforms\n\ngithub: JasonTaylorDev # Replace with up to 4 GitHub Sponsors-enabled use"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 537,
"preview": "---\nname: Bug report 🐛\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 213,
"preview": "blank_issues_enabled: true\ncontact_links:\n - name: Ask a question ❓\n url: https://github.com/jasontaylordev/cleanarc"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 597,
"preview": "---\nname: Feature request ✨\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your f"
},
{
"path": ".github/workflows/azure-dev.yml",
"chars": 1736,
"preview": "name: Azure deployment\n\non:\n workflow_dispatch:\n push:\n # Run when commits are pushed to mainline branch (main or m"
},
{
"path": ".github/workflows/build.yml",
"chars": 1553,
"preview": "name: Build\r\n\r\non:\r\n pull_request:\r\n branches: [ main ]\r\n paths-ignore:\r\n - '.scripts/**'\r\n - .gitignor"
},
{
"path": ".github/workflows/codeql.yml",
"chars": 750,
"preview": "name: CodeQL\n\non:\n push:\n branches: [ main ]\n paths-ignore:\n - .gitignore\n - CODE_OF_CONDUCT.md\n -"
},
{
"path": ".github/workflows/release.yml",
"chars": 1939,
"preview": "name: Release\n\non:\n release:\n types: [published]\n\npermissions:\n contents: read\n\njobs:\n publish:\n name: Publish "
},
{
"path": ".github/workflows/test-templates.yml",
"chars": 2111,
"preview": "name: Test Templates\n\non:\n push:\n branches: [ main ]\n workflow_dispatch:\n\npermissions:\n contents: read\n\njobs:\n te"
},
{
"path": ".gitignore",
"chars": 8173,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
},
{
"path": ".template.config/dotnetcli.host.json",
"chars": 293,
"preview": "{\n \"symbolInfo\": {\n \"ClientFramework\": {\n \"longName\": \"client-framework\",\n \"shortName\": \"cf\"\n },\n \"P"
},
{
"path": ".template.config/ide.host.json",
"chars": 765,
"preview": "{\n \"$schema\": \"http://json.schemastore.org/vs-2017.3.host\",\n \"order\": 0,\n \"icon\": \"icon.png\",\n \"symbolInfo\": [\n {"
},
{
"path": ".template.config/template.json",
"chars": 12652,
"preview": "{\n \"$schema\": \"http://json.schemastore.org/template\",\n \"author\": \"JasonTaylorDev\",\n \"classifications\": [\n \"Angular"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3357,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CleanArchitecture.nuspec",
"chars": 1922,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<package xmlns=\"http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd\">\r\n <"
},
{
"path": "CleanArchitecture.slnx",
"chars": 1281,
"preview": "<Solution>\n <Folder Name=\"/Solution Items/\">\n <File Path=\".editorconfig\" />\n <File Path=\".gitignore\" />\n <File"
},
{
"path": "Directory.Build.props",
"chars": 600,
"preview": "<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->\n<Project>\n <PropertyGr"
},
{
"path": "Directory.Packages.props",
"chars": 4272,
"preview": "<!-- For more info on central package management go to https://devblogs.microsoft.com/nuget/introducing-central-package-"
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2023 JasonTaylorDev\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README-template.md",
"chars": 1631,
"preview": "# CleanArchitecture\r\n\r\nThe project was generated using the [Clean.Architecture.Solution.Template](caRepositoryUrl) vers"
},
{
"path": "README.md",
"chars": 6791,
"preview": "# Clean Architecture Solution Template\r\n\r\n[\n\n$ErrorActionPreference = \"Stop\"\n\n$sol"
},
{
"path": "build/repack.ps1",
"chars": 412,
"preview": "$ErrorActionPreference = \"Stop\"\n\n$root = Split-Path $PSScriptRoot -Parent\n$name = \"Clean.Architecture.Solution.Template."
},
{
"path": "build/test.ps1",
"chars": 3382,
"preview": "param (\n [string[]]$ClientFramework = @(\"angular\", \"react\", \"none\"),\n [string[]]$Database = @(\"sqlite\", \"sqlserver"
},
{
"path": "global.json",
"chars": 85,
"preview": "{\r\n \"sdk\": {\r\n \"version\": \"10.0.201\",\r\n \"rollForward\": \"latestFeature\"\r\n }\r\n}"
},
{
"path": "infra/README.md",
"chars": 532,
"preview": "# Infrastructure (infra)\n\nThis folder contains infrastructure-as-code and deployment templates used with the **Azure Dev"
},
{
"path": "infra/abbreviations.json",
"chars": 5433,
"preview": "{\n \"analysisServicesServers\": \"as\",\n \"apiManagementService\": \"apim-\",\n \"appConfigurationStores\": \"appcs-\",\n "
},
{
"path": "infra/core/ai/cognitiveservices.bicep",
"chars": 1604,
"preview": "metadata description = 'Creates an Azure Cognitive Services instance.'\nparam name string\nparam location string = resourc"
},
{
"path": "infra/core/ai/hub-dependencies.bicep",
"chars": 4888,
"preview": "param location string = resourceGroup().location\nparam tags object = {}\n\n@description('Name of the key vault')\nparam key"
},
{
"path": "infra/core/ai/hub.bicep",
"chars": 3725,
"preview": "@description('The AI Studio Hub Resource name')\nparam name string\n@description('The display name of the AI Studio Hub Re"
},
{
"path": "infra/core/ai/project.bicep",
"chars": 2224,
"preview": "@description('The AI Studio Hub Resource name')\nparam name string\n@description('The display name of the AI Studio Hub Re"
},
{
"path": "infra/core/config/configstore.bicep",
"chars": 1440,
"preview": "metadata description = 'Creates an Azure App Configuration store.'\n\n@description('The name for the Azure App Configurati"
},
{
"path": "infra/core/database/cosmos/cosmos-account.bicep",
"chars": 1438,
"preview": "metadata description = 'Creates an Azure Cosmos DB account.'\nparam name string\nparam location string = resourceGroup().l"
},
{
"path": "infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep",
"chars": 663,
"preview": "metadata description = 'Creates an Azure Cosmos DB for MongoDB account.'\nparam name string\nparam location string = resou"
},
{
"path": "infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep",
"chars": 1235,
"preview": "metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.'\nparam accountName string\nparam "
},
{
"path": "infra/core/database/cosmos/sql/cosmos-sql-account.bicep",
"chars": 598,
"preview": "metadata description = 'Creates an Azure Cosmos DB for NoSQL account.'\nparam name string\nparam location string = resourc"
},
{
"path": "infra/core/database/cosmos/sql/cosmos-sql-db.bicep",
"chars": 1878,
"preview": "metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.'\nparam accountName string\nparam da"
},
{
"path": "infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep",
"chars": 550,
"preview": "metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.'\nparam accountName string\n\nparam"
},
{
"path": "infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep",
"chars": 863,
"preview": "metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.'\nparam accountName string\n\nresou"
},
{
"path": "infra/core/database/mysql/flexibleserver.bicep",
"chars": 1656,
"preview": "metadata description = 'Creates an Azure Database for MySQL - Flexible Server.'\nparam name string\nparam location string "
},
{
"path": "infra/core/database/postgresql/flexibleserver.bicep",
"chars": 3997,
"preview": "metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.'\nparam name string\nparam location st"
},
{
"path": "infra/core/database/sqlserver/sqlserver.bicep",
"chars": 5029,
"preview": "metadata description = 'Creates an Azure SQL Server instance.'\nparam name string\nparam location string = resourceGroup()"
},
{
"path": "infra/core/gateway/apim.bicep",
"chars": 3305,
"preview": "metadata description = 'Creates an Azure API Management instance.'\nparam name string\nparam location string = resourceGro"
},
{
"path": "infra/core/host/ai-environment.bicep",
"chars": 3702,
"preview": "@minLength(1)\n@description('Primary location for all resources')\nparam location string\n\n@description('The AI Hub resourc"
},
{
"path": "infra/core/host/aks-agent-pool.bicep",
"chars": 495,
"preview": "metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.'\nparam clusterName string\n\n@des"
},
{
"path": "infra/core/host/aks-managed-cluster.bicep",
"chars": 4190,
"preview": "metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.'\n@description('The n"
},
{
"path": "infra/core/host/aks.bicep",
"chars": 8356,
"preview": "metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additio"
},
{
"path": "infra/core/host/appservice-appsettings.bicep",
"chars": 501,
"preview": "metadata description = 'Updates app settings for an Azure App Service.'\n@description('The name of the app service resour"
},
{
"path": "infra/core/host/appservice.bicep",
"chars": 5405,
"preview": "metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.'\nparam name string\nparam loc"
},
{
"path": "infra/core/host/appserviceplan.bicep",
"chars": 970,
"preview": "metadata description = 'Creates an Azure App Service plan.'\nparam name string\nparam location string = resourceGroup().lo"
},
{
"path": "infra/core/host/container-app-upsert.bicep",
"chars": 3606,
"preview": "metadata description = 'Creates or updates an existing Azure Container App.'\nparam name string\nparam location string = r"
},
{
"path": "infra/core/host/container-app.bicep",
"chars": 6012,
"preview": "metadata description = 'Creates a container app in an Azure Container App environment.'\nparam name string\nparam location"
},
{
"path": "infra/core/host/container-apps-environment.bicep",
"chars": 1450,
"preview": "metadata description = 'Creates an Azure Container Apps environment.'\nparam name string\nparam location string = resource"
},
{
"path": "infra/core/host/container-apps.bicep",
"chars": 1548,
"preview": "metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.'\nparam name string\n"
},
{
"path": "infra/core/host/container-registry.bicep",
"chars": 3657,
"preview": "metadata description = 'Creates an Azure Container Registry.'\nparam name string\nparam location string = resourceGroup()."
},
{
"path": "infra/core/host/functions.bicep",
"chars": 3535,
"preview": "metadata description = 'Creates an Azure Function in an existing Azure App Service plan.'\nparam name string\nparam locati"
},
{
"path": "infra/core/host/staticwebapp.bicep",
"chars": 467,
"preview": "metadata description = 'Creates an Azure Static Web Apps instance.'\nparam name string\nparam location string = resourceGr"
},
{
"path": "infra/core/monitor/applicationinsights-dashboard.bicep",
"chars": 43812,
"preview": "metadata description = 'Creates a dashboard for an Application Insights instance.'\nparam name string\nparam applicationIn"
},
{
"path": "infra/core/monitor/applicationinsights.bicep",
"chars": 1029,
"preview": "metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.'\nparam na"
},
{
"path": "infra/core/monitor/loganalytics.bicep",
"chars": 498,
"preview": "metadata description = 'Creates a Log Analytics workspace.'\nparam name string\nparam location string = resourceGroup().lo"
},
{
"path": "infra/core/monitor/monitoring.bicep",
"chars": 1197,
"preview": "metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.'\nparam logAnalyticsName "
},
{
"path": "infra/core/networking/cdn-endpoint.bicep",
"chars": 1298,
"preview": "metadata description = 'Adds an endpoint to an Azure CDN profile.'\nparam name string\nparam location string = resourceGro"
},
{
"path": "infra/core/networking/cdn-profile.bicep",
"chars": 814,
"preview": "metadata description = 'Creates an Azure CDN profile.'\nparam name string\nparam location string = resourceGroup().locatio"
},
{
"path": "infra/core/networking/cdn.bicep",
"chars": 1115,
"preview": "metadata description = 'Creates an Azure CDN profile with a single endpoint.'\nparam location string = resourceGroup().lo"
},
{
"path": "infra/core/search/search-services.bicep",
"chars": 1717,
"preview": "metadata description = 'Creates an Azure AI Search instance.'\nparam name string\nparam location string = resourceGroup()."
},
{
"path": "infra/core/security/aks-managed-cluster-access.bicep",
"chars": 1068,
"preview": "metadata description = 'Assigns RBAC role to the specified AKS cluster and principal.'\n\n@description('The AKS cluster na"
},
{
"path": "infra/core/security/configstore-access.bicep",
"chars": 803,
"preview": "@description('Name of Azure App Configuration store')\nparam configStoreName string\n\n@description('The principal ID of th"
},
{
"path": "infra/core/security/keyvault-access.bicep",
"chars": 581,
"preview": "metadata description = 'Assigns an Azure Key Vault access policy.'\nparam name string = 'add'\n\nparam keyVaultName string\n"
},
{
"path": "infra/core/security/keyvault-secret.bicep",
"chars": 789,
"preview": "metadata description = 'Creates or updates a secret in an Azure Key Vault.'\nparam name string\nparam tags object = {}\npar"
},
{
"path": "infra/core/security/keyvault.bicep",
"chars": 1523,
"preview": "metadata description = 'Creates an Azure Key Vault.'\nparam name string\nparam location string = resourceGroup().location\n"
},
{
"path": "infra/core/security/registry-access.bicep",
"chars": 792,
"preview": "metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.'\nparam containerRegistryName"
},
{
"path": "infra/core/security/role.bicep",
"chars": 595,
"preview": "metadata description = 'Creates a role assignment for a service principal.'\nparam principalId string\n\n@allowed([\n 'Devi"
},
{
"path": "infra/core/storage/storage-account.bicep",
"chars": 2851,
"preview": "metadata description = 'Creates an Azure storage account.'\nparam name string\nparam location string = resourceGroup().loc"
},
{
"path": "infra/core/testing/loadtesting.bicep",
"chars": 379,
"preview": "param name string\nparam location string = resourceGroup().location\nparam managedIdentity bool = false\nparam tags object "
},
{
"path": "infra/main.bicep",
"chars": 5886,
"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": 620,
"preview": "{\n \"$schema\": \"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#\",\n \"contentVersio"
},
{
"path": "infra/services/web.bicep",
"chars": 1034,
"preview": "param name string\nparam location string = resourceGroup().location\nparam tags object = {}\n\nparam serviceName string = 'w"
},
{
"path": "renovate.json",
"chars": 1429,
"preview": "{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\n \"config:recommended\",\n \":seman"
},
{
"path": "src/AppHost/AppHost.csproj",
"chars": 906,
"preview": "<Project Sdk=\"Aspire.AppHost.Sdk/13.1.3\">\n\n <PropertyGroup>\n <OutputType>Exe</OutputType>\n <RootNamespace>CleanA"
},
{
"path": "src/AppHost/Program.cs",
"chars": 964,
"preview": "using CleanArchitecture.Shared;\n\nvar builder = DistributedApplication.CreateBuilder(args);\n\n#if (UsePostgreSQL)\nvar data"
},
{
"path": "src/AppHost/Properties/launchSettings.json",
"chars": 1426,
"preview": "{\n \"$schema\": \"https://json.schemastore.org/launchsettings.json\",\n \"profiles\": {\n \"https\": {\n \"commandName\": \""
},
{
"path": "src/AppHost/appsettings.Development.json",
"chars": 119,
"preview": "{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Information\",\n \"Microsoft.AspNetCore\": \"Warning\"\n }\n }\n}\n"
},
{
"path": "src/AppHost/appsettings.json",
"chars": 158,
"preview": "{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Information\",\n \"Microsoft.AspNetCore\": \"Warning\",\n \"Aspir"
},
{
"path": "src/Application/Application.csproj",
"chars": 839,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n <PropertyGroup>\r\n <RootNamespace>CleanArchitecture.Application</RootNamespace"
},
{
"path": "src/Application/Common/Behaviours/AuthorizationBehaviour.cs",
"chars": 2768,
"preview": "using System.Reflection;\nusing CleanArchitecture.Application.Common.Exceptions;\nusing CleanArchitecture.Application.Com"
},
{
"path": "src/Application/Common/Behaviours/LoggingBehaviour.cs",
"chars": 1142,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing MediatR.Pipeline;\nusing Microsoft.Extensions.Logging;\n\nnam"
},
{
"path": "src/Application/Common/Behaviours/PerformanceBehaviour.cs",
"chars": 1664,
"preview": "using System.Diagnostics;\nusing CleanArchitecture.Application.Common.Interfaces;\nusing Microsoft.Extensions.Logging;\n\nn"
},
{
"path": "src/Application/Common/Behaviours/UnhandledExceptionBehaviour.cs",
"chars": 858,
"preview": "using Microsoft.Extensions.Logging;\n\nnamespace CleanArchitecture.Application.Common.Behaviours;\n\npublic class Unhandled"
},
{
"path": "src/Application/Common/Behaviours/ValidationBehaviour.cs",
"chars": 1159,
"preview": "using ValidationException = CleanArchitecture.Application.Common.Exceptions.ValidationException;\n\nnamespace CleanArchit"
},
{
"path": "src/Application/Common/Exceptions/ForbiddenAccessException.cs",
"chars": 166,
"preview": "namespace CleanArchitecture.Application.Common.Exceptions;\n\npublic class ForbiddenAccessException : Exception\n{\n pub"
},
{
"path": "src/Application/Common/Exceptions/ValidationException.cs",
"chars": 656,
"preview": "using FluentValidation.Results;\n\nnamespace CleanArchitecture.Application.Common.Exceptions;\n\npublic class ValidationExc"
},
{
"path": "src/Application/Common/Interfaces/IApplicationDbContext.cs",
"chars": 295,
"preview": "using CleanArchitecture.Domain.Entities;\n\nnamespace CleanArchitecture.Application.Common.Interfaces;\n\npublic interface "
},
{
"path": "src/Application/Common/Interfaces/IIdentityService.cs",
"chars": 470,
"preview": "using CleanArchitecture.Application.Common.Models;\n\nnamespace CleanArchitecture.Application.Common.Interfaces;\n\npublic "
},
{
"path": "src/Application/Common/Interfaces/IUser.cs",
"chars": 146,
"preview": "namespace CleanArchitecture.Application.Common.Interfaces;\n\npublic interface IUser\n{\n string? Id { get; }\n List<s"
},
{
"path": "src/Application/Common/Mappings/MappingExtensions.cs",
"chars": 825,
"preview": "using CleanArchitecture.Application.Common.Models;\n\nnamespace CleanArchitecture.Application.Common.Mappings;\n\npublic st"
},
{
"path": "src/Application/Common/Models/LookupDto.cs",
"chars": 386,
"preview": "using CleanArchitecture.Domain.Entities;\n\nnamespace CleanArchitecture.Application.Common.Models;\n\npublic class LookupDt"
},
{
"path": "src/Application/Common/Models/PaginatedList.cs",
"chars": 1068,
"preview": "namespace CleanArchitecture.Application.Common.Models;\n\npublic class PaginatedList<T>\n{\n public IReadOnlyCollection<"
},
{
"path": "src/Application/Common/Models/Result.cs",
"chars": 528,
"preview": "namespace CleanArchitecture.Application.Common.Models;\n\npublic class Result\n{\n internal Result(bool succeeded, IEnum"
},
{
"path": "src/Application/Common/Security/AuthorizeAttribute.cs",
"chars": 806,
"preview": "namespace CleanArchitecture.Application.Common.Security;\n\n/// <summary>\n/// Specifies the class this attribute is appli"
},
{
"path": "src/Application/DependencyInjection.cs",
"chars": 984,
"preview": "using System.Reflection;\nusing CleanArchitecture.Application.Common.Behaviours;\nusing Microsoft.Extensions.Hosting;\n\nna"
},
{
"path": "src/Application/GlobalUsings.cs",
"chars": 202,
"preview": "global using Ardalis.GuardClauses;\nglobal using AutoMapper;\nglobal using AutoMapper.QueryableExtensions;\nglobal using M"
},
{
"path": "src/Application/TodoItems/Commands/CreateTodoItem/CreateTodoItem.cs",
"chars": 1067,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Domain.Entities;\nusing CleanArchitecture"
},
{
"path": "src/Application/TodoItems/Commands/CreateTodoItem/CreateTodoItemCommandValidator.cs",
"chars": 311,
"preview": "namespace CleanArchitecture.Application.TodoItems.Commands.CreateTodoItem;\n\npublic class CreateTodoItemCommandValidator"
},
{
"path": "src/Application/TodoItems/Commands/DeleteTodoItem/DeleteTodoItem.cs",
"chars": 934,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Domain.Events;\n\nnamespace CleanArchitect"
},
{
"path": "src/Application/TodoItems/Commands/UpdateTodoItem/UpdateTodoItem.cs",
"chars": 965,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\n\nnamespace CleanArchitecture.Application.TodoItems.Commands.Upda"
},
{
"path": "src/Application/TodoItems/Commands/UpdateTodoItem/UpdateTodoItemCommandValidator.cs",
"chars": 311,
"preview": "namespace CleanArchitecture.Application.TodoItems.Commands.UpdateTodoItem;\n\npublic class UpdateTodoItemCommandValidator"
},
{
"path": "src/Application/TodoItems/Commands/UpdateTodoItemDetail/UpdateTodoItemDetail.cs",
"chars": 1135,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Domain.Enums;\n\nnamespace CleanArchitectu"
},
{
"path": "src/Application/TodoItems/EventHandlers/LogTodoItemCompleted.cs",
"chars": 653,
"preview": "using CleanArchitecture.Domain.Events;\nusing Microsoft.Extensions.Logging;\n\nnamespace CleanArchitecture.Application.Tod"
},
{
"path": "src/Application/TodoItems/EventHandlers/LogTodoItemCreated.cs",
"chars": 641,
"preview": "using CleanArchitecture.Domain.Events;\nusing Microsoft.Extensions.Logging;\n\nnamespace CleanArchitecture.Application.Tod"
},
{
"path": "src/Application/TodoItems/Queries/GetTodoItemsWithPagination/GetTodoItemsWithPagination.cs",
"chars": 1308,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Application.Common.Mappings;\nusing Clean"
},
{
"path": "src/Application/TodoItems/Queries/GetTodoItemsWithPagination/GetTodoItemsWithPaginationQueryValidator.cs",
"chars": 626,
"preview": "namespace CleanArchitecture.Application.TodoItems.Queries.GetTodoItemsWithPagination;\n\npublic class GetTodoItemsWithPag"
},
{
"path": "src/Application/TodoItems/Queries/GetTodoItemsWithPagination/TodoItemBriefDto.cs",
"chars": 460,
"preview": "using CleanArchitecture.Domain.Entities;\n\nnamespace CleanArchitecture.Application.TodoItems.Queries.GetTodoItemsWithPag"
},
{
"path": "src/Application/TodoLists/Commands/CreateTodoList/CreateTodoList.cs",
"chars": 848,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Domain.Entities;\n\nnamespace CleanArchite"
},
{
"path": "src/Application/TodoLists/Commands/CreateTodoList/CreateTodoListCommandValidator.cs",
"chars": 835,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\n\nnamespace CleanArchitecture.Application.TodoLists.Commands.Crea"
},
{
"path": "src/Application/TodoLists/Commands/DeleteTodoList/DeleteTodoList.cs",
"chars": 854,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\n\nnamespace CleanArchitecture.Application.TodoLists.Commands.Dele"
},
{
"path": "src/Application/TodoLists/Commands/PurgeTodoLists/PurgeTodoLists.cs",
"chars": 841,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Application.Common.Security;\nusing Clean"
},
{
"path": "src/Application/TodoLists/Commands/UpdateTodoList/UpdateTodoList.cs",
"chars": 893,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\n\nnamespace CleanArchitecture.Application.TodoLists.Commands.Upda"
},
{
"path": "src/Application/TodoLists/Commands/UpdateTodoList/UpdateTodoListCommandValidator.cs",
"chars": 906,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\n\nnamespace CleanArchitecture.Application.TodoLists.Commands.Upda"
},
{
"path": "src/Application/TodoLists/Queries/GetTodos/GetTodos.cs",
"chars": 1257,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Application.Common.Models;\nusing CleanAr"
},
{
"path": "src/Application/TodoLists/Queries/GetTodos/TodoItemDto.cs",
"chars": 598,
"preview": "using CleanArchitecture.Domain.Entities;\n\nnamespace CleanArchitecture.Application.TodoLists.Queries.GetTodos;\n\npublic c"
},
{
"path": "src/Application/TodoLists/Queries/GetTodos/TodoListDto.cs",
"chars": 547,
"preview": "using CleanArchitecture.Domain.Entities;\n\nnamespace CleanArchitecture.Application.TodoLists.Queries.GetTodos;\n\npublic c"
},
{
"path": "src/Application/TodoLists/Queries/GetTodos/TodosVm.cs",
"chars": 343,
"preview": "using CleanArchitecture.Application.Common.Models;\n\nnamespace CleanArchitecture.Application.TodoLists.Queries.GetTodos;"
},
{
"path": "src/Application/WeatherForecasts/Queries/GetWeatherForecasts/GetWeatherForecastsQuery.cs",
"chars": 1090,
"preview": "namespace CleanArchitecture.Application.WeatherForecasts.Queries.GetWeatherForecasts;\n\npublic record GetWeatherForecast"
},
{
"path": "src/Application/WeatherForecasts/Queries/GetWeatherForecasts/WeatherForecast.cs",
"chars": 330,
"preview": "namespace CleanArchitecture.Application.WeatherForecasts.Queries.GetWeatherForecasts;\n\npublic class WeatherForecast\n{\n "
},
{
"path": "src/Domain/Common/BaseAuditableEntity.cs",
"chars": 299,
"preview": "namespace CleanArchitecture.Domain.Common;\n\npublic abstract class BaseAuditableEntity : BaseEntity\n{\n public DateTim"
},
{
"path": "src/Domain/Common/BaseEntity.cs",
"chars": 788,
"preview": "using System.ComponentModel.DataAnnotations.Schema;\n\nnamespace CleanArchitecture.Domain.Common;\n\npublic abstract class "
},
{
"path": "src/Domain/Common/BaseEvent.cs",
"chars": 113,
"preview": "using MediatR;\n\nnamespace CleanArchitecture.Domain.Common;\n\npublic abstract class BaseEvent : INotification\n{\n}\n"
},
{
"path": "src/Domain/Common/ValueObject.cs",
"chars": 1437,
"preview": "namespace CleanArchitecture.Domain.Common;\n\n// Learn more: https://docs.microsoft.com/en-us/dotnet/standard/microservic"
},
{
"path": "src/Domain/Constants/Policies.cs",
"chars": 135,
"preview": "namespace CleanArchitecture.Domain.Constants;\n\npublic abstract class Policies\n{\n public const string CanPurge = name"
},
{
"path": "src/Domain/Constants/Roles.cs",
"chars": 142,
"preview": "namespace CleanArchitecture.Domain.Constants;\n\npublic abstract class Roles\n{\n public const string Administrator = na"
},
{
"path": "src/Domain/Domain.csproj",
"chars": 298,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n <PropertyGroup>\r\n <RootNamespace>CleanArchitecture.Domain</RootNamespace>\r\n "
},
{
"path": "src/Domain/Entities/TodoItem.cs",
"chars": 620,
"preview": "namespace CleanArchitecture.Domain.Entities;\n\npublic class TodoItem : BaseAuditableEntity\n{\n public int ListId { get"
},
{
"path": "src/Domain/Entities/TodoList.cs",
"chars": 270,
"preview": "namespace CleanArchitecture.Domain.Entities;\n\npublic class TodoList : BaseAuditableEntity\n{\n public string? Title { "
},
{
"path": "src/Domain/Enums/PriorityLevel.cs",
"chars": 130,
"preview": "namespace CleanArchitecture.Domain.Enums;\n\npublic enum PriorityLevel\n{\n None = 0,\n Low = 1,\n Medium = 2,\n H"
},
{
"path": "src/Domain/Events/TodoItemCompletedEvent.cs",
"chars": 214,
"preview": "namespace CleanArchitecture.Domain.Events;\n\npublic class TodoItemCompletedEvent : BaseEvent\n{\n public TodoItemComple"
},
{
"path": "src/Domain/Events/TodoItemCreatedEvent.cs",
"chars": 210,
"preview": "namespace CleanArchitecture.Domain.Events;\n\npublic class TodoItemCreatedEvent : BaseEvent\n{\n public TodoItemCreatedE"
},
{
"path": "src/Domain/Events/TodoItemDeletedEvent.cs",
"chars": 210,
"preview": "namespace CleanArchitecture.Domain.Events;\n\npublic class TodoItemDeletedEvent : BaseEvent\n{\n public TodoItemDeletedE"
},
{
"path": "src/Domain/Exceptions/UnsupportedColourException.cs",
"chars": 221,
"preview": "namespace CleanArchitecture.Domain.Exceptions;\n\npublic class UnsupportedColourException : Exception\n{\n public Unsupp"
},
{
"path": "src/Domain/GlobalUsings.cs",
"chars": 287,
"preview": "global using CleanArchitecture.Domain.Common;\nglobal using CleanArchitecture.Domain.Entities;\nglobal using CleanArchite"
},
{
"path": "src/Domain/ValueObjects/Colour.cs",
"chars": 1586,
"preview": "namespace CleanArchitecture.Domain.ValueObjects;\n\npublic class Colour(string code) : ValueObject\n{\n public static Co"
},
{
"path": "src/Infrastructure/Data/ApplicationDbContext.cs",
"chars": 830,
"preview": "using System.Reflection;\nusing CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Domain.Entities"
},
{
"path": "src/Infrastructure/Data/ApplicationDbContextInitialiser.cs",
"chars": 3567,
"preview": "using CleanArchitecture.Domain.Constants;\nusing CleanArchitecture.Domain.Entities;\nusing CleanArchitecture.Infrastructu"
},
{
"path": "src/Infrastructure/Data/Configurations/TodoItemConfiguration.cs",
"chars": 448,
"preview": "using CleanArchitecture.Domain.Entities;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metad"
},
{
"path": "src/Infrastructure/Data/Configurations/TodoListConfiguration.cs",
"chars": 502,
"preview": "using CleanArchitecture.Domain.Entities;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metad"
},
{
"path": "src/Infrastructure/Data/Interceptors/AuditableEntityInterceptor.cs",
"chars": 2185,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Domain.Common;\nusing Microsoft.EntityFra"
},
{
"path": "src/Infrastructure/Data/Interceptors/DispatchDomainEventsInterceptor.cs",
"chars": 1585,
"preview": "using CleanArchitecture.Domain.Common;\nusing MediatR;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFramew"
},
{
"path": "src/Infrastructure/DependencyInjection.cs",
"chars": 3288,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\r\nusing CleanArchitecture.Domain.Constants;\r\nusing CleanArchitect"
},
{
"path": "src/Infrastructure/GlobalUsings.cs",
"chars": 74,
"preview": "global using Ardalis.GuardClauses;\nglobal using CleanArchitecture.Shared;"
},
{
"path": "src/Infrastructure/Identity/ApplicationUser.cs",
"chars": 141,
"preview": "using Microsoft.AspNetCore.Identity;\n\nnamespace CleanArchitecture.Infrastructure.Identity;\n\npublic class ApplicationUse"
},
{
"path": "src/Infrastructure/Identity/IdentityResultExtensions.cs",
"chars": 413,
"preview": "using CleanArchitecture.Application.Common.Models;\nusing Microsoft.AspNetCore.Identity;\n\nnamespace CleanArchitecture.In"
},
{
"path": "src/Infrastructure/Identity/IdentityService.cs",
"chars": 2517,
"preview": "using CleanArchitecture.Application.Common.Interfaces;\nusing CleanArchitecture.Application.Common.Models;\nusing Microsof"
},
{
"path": "src/Infrastructure/Infrastructure.csproj",
"chars": 1174,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n <PropertyGroup>\r\n <RootNamespace>CleanArchitecture.Infrastructure</RootNamesp"
},
{
"path": "src/ServiceDefaults/Extensions.cs",
"chars": 4529,
"preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Diagnostics.HealthChecks;\nusing Microsoft.Extensions.Depe"
},
{
"path": "src/ServiceDefaults/ServiceDefaults.csproj",
"chars": 886,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <IsAspireSharedProject>true</IsAspireSharedProject>\n <RootN"
},
{
"path": "src/Shared/Services.cs",
"chars": 948,
"preview": "namespace CleanArchitecture.Shared;\n\npublic static class Services\n{\n /// <summary>\n /// The name of the Web Front"
},
{
"path": "src/Shared/Shared.csproj",
"chars": 364,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <RootNamespace>CleanArchitecture.Shared</RootNamespace>\n <A"
},
{
"path": "src/Web/ClientApp/.editorconfig",
"chars": 313,
"preview": "# Editor configuration, see http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = "
},
{
"path": "src/Web/ClientApp/.gitignore",
"chars": 526,
"preview": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\n/dist\n/dist-server\n/tmp\n/ou"
},
{
"path": "src/Web/ClientApp/Dockerfile",
"chars": 407,
"preview": "FROM node:24 as build\n\nWORKDIR /app\n\nCOPY package.json package.json\nCOPY package-lock.json package-lock.json\n\nRUN npm in"
},
{
"path": "src/Web/ClientApp/README.md",
"chars": 1075,
"preview": "# CleanArchitecture.Web\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 2"
},
{
"path": "src/Web/ClientApp/angular.json",
"chars": 3269,
"preview": "{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"version\": 1,\n \"newProjectRoot\": \"projects\",\n \""
},
{
"path": "src/Web/ClientApp/default.conf.template",
"chars": 459,
"preview": "server {\n listen ${PORT};\n listen [::]:${PORT};\n server_name localhost;\n\n access_log /var/log/nginx"
},
{
"path": "src/Web/ClientApp/karma.conf.js",
"chars": 1427,
"preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
},
{
"path": "src/Web/ClientApp/nswag.json",
"chars": 2394,
"preview": "{\n \"runtime\": \"Net100\",\n \"defaultVariables\": null,\n \"documentGenerator\": {\n \"fromDocument\": {\n \"json\": null,\n"
},
{
"path": "src/Web/ClientApp/package.json",
"chars": 1471,
"preview": "{\n \"name\": \"cleanarchitecture.web\",\n \"version\": \"0.0.0\",\n \"scripts\": {\n \"ng\": \"ng\",\n \"start\": \"run-script-os\",\n"
},
{
"path": "src/Web/ClientApp/proxy.conf.js",
"chars": 383,
"preview": "const { env } = require('process');\n\nconst target =\n env[\"services__webapi__https__0\"] ||\n env[\"services__webapi__http"
},
{
"path": "src/Web/ClientApp/src/api-authorization/auth.guard.ts",
"chars": 776,
"preview": "import { Injectable } from '@angular/core';\nimport { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } "
},
{
"path": "src/Web/ClientApp/src/api-authorization/auth.service.ts",
"chars": 1223,
"preview": "import { Injectable } from '@angular/core';\nimport { BehaviorSubject, Observable, of } from 'rxjs';\nimport { tap, catchE"
},
{
"path": "src/Web/ClientApp/src/api-authorization/authorize.interceptor.spec.ts",
"chars": 508,
"preview": "import { TestBed, inject } from '@angular/core/testing';\nimport { provideRouter } from '@angular/router';\n\nimport { Auth"
},
{
"path": "src/Web/ClientApp/src/api-authorization/authorize.interceptor.ts",
"chars": 1000,
"preview": "import { Injectable } from '@angular/core';\nimport { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResp"
},
{
"path": "src/Web/ClientApp/src/api-authorization/login/login.component.html",
"chars": 892,
"preview": "<article>\n <h2>Log in</h2>\n <form (ngSubmit)=\"login()\" #loginForm=\"ngForm\">\n <label for=\"email\">Email</label>\n <"
},
{
"path": "src/Web/ClientApp/src/api-authorization/login/login.component.ts",
"chars": 894,
"preview": "import { Component, ChangeDetectorRef } from '@angular/core';\nimport { Router, ActivatedRoute } from '@angular/router';\n"
},
{
"path": "src/Web/ClientApp/src/api-authorization/register/register.component.html",
"chars": 1159,
"preview": "<article>\n <h2>Register</h2>\n @if (error) {\n <p class=\"error\">{{ error }}</p>\n }\n <form (ngSubmit)=\"register()\">\n"
},
{
"path": "src/Web/ClientApp/src/api-authorization/register/register.component.ts",
"chars": 1197,
"preview": "import { Component, ChangeDetectorRef } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { AuthSer"
},
{
"path": "src/Web/ClientApp/src/app/app.component.html",
"chars": 79,
"preview": "<app-nav-menu></app-nav-menu>\n<main>\n <router-outlet></router-outlet>\n</main>\n"
},
{
"path": "src/Web/ClientApp/src/app/app.component.ts",
"chars": 190,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n standalone: false,\n selector: 'app-root',\n templateUrl: './"
},
{
"path": "src/Web/ClientApp/src/app/app.module.ts",
"chars": 2769,
"preview": "import { APP_ID, NgModule, inject, provideAppInitializer } from '@angular/core';\nimport { BrowserModule } from '@angular"
},
{
"path": "src/Web/ClientApp/src/app/app.server.module.ts",
"chars": 308,
"preview": "import { NgModule } from '@angular/core';\nimport { ServerModule } from '@angular/platform-server';\nimport { AppComponent"
},
{
"path": "src/Web/ClientApp/src/app/counter/counter.component.html",
"chars": 210,
"preview": "<h1>Counter</h1>\n\n<p>This is a simple example of an Angular component.</p>\n\n<p aria-live=\"polite\">Current count: <strong"
},
{
"path": "src/Web/ClientApp/src/app/counter/counter.component.spec.ts",
"chars": 1083,
"preview": "import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';\n\nimport { CounterComponent } from './co"
},
{
"path": "src/Web/ClientApp/src/app/counter/counter.component.ts",
"chars": 281,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n standalone: false,\n selector: 'app-counter-component',\n tem"
},
{
"path": "src/Web/ClientApp/src/app/fetch-data/fetch-data.component.html",
"chars": 641,
"preview": "<h1 id=\"tableLabel\">Weather forecast</h1>\n\n<p>This component demonstrates fetching data from the server.</p>\n@if (!forec"
},
{
"path": "src/Web/ClientApp/src/app/fetch-data/fetch-data.component.ts",
"chars": 649,
"preview": "import { ChangeDetectorRef, Component } from '@angular/core';\nimport { WeatherForecastsClient, WeatherForecast } from '."
},
{
"path": "src/Web/ClientApp/src/app/home/home.component.html",
"chars": 1565,
"preview": "<h1>Welcome</h1>\n<p>A full-stack application with an <a href='https://angular.dev/'>Angular</a> frontend and an <a href="
},
{
"path": "src/Web/ClientApp/src/app/home/home.component.ts",
"chars": 176,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n standalone: false,\n selector: 'app-home',\n templateUrl: './"
},
{
"path": "src/Web/ClientApp/src/app/nav-menu/nav-menu.component.html",
"chars": 997,
"preview": "<header>\n <nav>\n <ul>\n <li><a class=\"nav-brand\" [routerLink]=\"['/']\">Clean Architecture</a></li>\n </ul>\n "
},
{
"path": "src/Web/ClientApp/src/app/nav-menu/nav-menu.component.scss",
"chars": 42,
"preview": "// Styles handled globally in styles.scss\n"
},
{
"path": "src/Web/ClientApp/src/app/nav-menu/nav-menu.component.ts",
"chars": 635,
"preview": "import { Component } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { AuthService } from 'src/ap"
},
{
"path": "src/Web/ClientApp/src/app/theme-toggle/theme-toggle.component.html",
"chars": 166,
"preview": "<button class=\"theme-toggle-btn\" (click)=\"cycle()\" [attr.aria-label]=\"label()\">\n <lucide-icon [name]=\"icon()\" [size]=\"2"
},
{
"path": "src/Web/ClientApp/src/app/theme-toggle/theme-toggle.component.ts",
"chars": 864,
"preview": "import { Component, computed } from '@angular/core';\nimport { ThemeService, Theme } from '../theme.service';\n\n@Component"
},
{
"path": "src/Web/ClientApp/src/app/theme.service.ts",
"chars": 822,
"preview": "import { Injectable, signal } from '@angular/core';\n\nexport type Theme = 'auto' | 'light' | 'dark';\n\n@Injectable({ provi"
},
{
"path": "src/Web/ClientApp/src/app/todo/todo.component.html",
"chars": 5893,
"preview": "<hgroup>\n <h1>Tasks</h1>\n <p>Manage your todo lists and tasks.</p>\n</hgroup>\n\n@if (!lists()) {\n <span aria-busy=\"true"
},
{
"path": "src/Web/ClientApp/src/app/todo/todo.component.scss",
"chars": 42,
"preview": "// Styles handled globally in styles.scss\n"
},
{
"path": "src/Web/ClientApp/src/app/todo/todo.component.ts",
"chars": 9248,
"preview": "import { Component, OnInit, ViewChild, ElementRef, signal, computed, effect } from '@angular/core';\nimport { TodoListsCl"
}
]
// ... and 111 more files (download for full content)
About this extraction
This page contains the full source code of the jasontaylordev/CleanArchitecture GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 311 files (488.1 KB), approximately 123.5k tokens, and a symbol index with 477 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.