Merge branch 'main' into tw-bootstrap
This commit is contained in:
@@ -1,82 +0,0 @@
|
|||||||
|
|
||||||
#-------------------------------------------------------------------------------------------------------------
|
|
||||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
|
|
||||||
#-------------------------------------------------------------------------------------------------------------
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:7.0
|
|
||||||
|
|
||||||
|
|
||||||
# This Dockerfile adds a non-root user with sudo access. Use the "remoteUser"
|
|
||||||
# property in devcontainer.json to use it. On Linux, the container user's GID/UIDs
|
|
||||||
# will be updated to match your local UID/GID (when using the dockerFile property).
|
|
||||||
# See https://aka.ms/vscode-remote/containers/non-root-user for details.
|
|
||||||
ARG USERNAME=vscode
|
|
||||||
ARG USER_UID=1000
|
|
||||||
ARG USER_GID=$USER_UID
|
|
||||||
|
|
||||||
# [Optional] Version of Node.js to install.
|
|
||||||
ARG INSTALL_NODE="false"
|
|
||||||
ARG NODE_VERSION="lts/*"
|
|
||||||
ENV NVM_DIR=/usr/local/share/nvm
|
|
||||||
|
|
||||||
# [Optional] Install the Azure CLI
|
|
||||||
ARG INSTALL_AZURE_CLI="false"
|
|
||||||
|
|
||||||
# Configure apt and install packages
|
|
||||||
RUN apt-get update \
|
|
||||||
&& export DEBIAN_FRONTEND=noninteractive \
|
|
||||||
&& apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \
|
|
||||||
#
|
|
||||||
# Verify git, process tools, lsb-release (common in install instructions for CLIs) installed
|
|
||||||
&& apt-get -y install git openssh-client less iproute2 procps apt-transport-https gnupg2 curl lsb-release \
|
|
||||||
#
|
|
||||||
# Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user.
|
|
||||||
&& groupadd --gid $USER_GID $USERNAME \
|
|
||||||
&& useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
|
|
||||||
# [Optional] Add sudo support for the non-root user
|
|
||||||
&& apt-get install -y sudo \
|
|
||||||
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
|
|
||||||
&& chmod 0440 /etc/sudoers.d/$USERNAME \
|
|
||||||
#
|
|
||||||
# [Optional] Install Node.js for ASP.NET Core Web Applicationss
|
|
||||||
&& if [ "$INSTALL_NODE" = "true" ]; then \
|
|
||||||
#
|
|
||||||
# Install nvm and Node
|
|
||||||
mkdir -p ${NVM_DIR} \
|
|
||||||
&& curl -so- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash 2>&1 \
|
|
||||||
&& chown -R ${USER_UID}:${USER_GID} ${NVM_DIR} \
|
|
||||||
&& /bin/bash -c "source $NVM_DIR/nvm.sh \
|
|
||||||
&& nvm alias default ${NODE_VERSION}" 2>&1 \
|
|
||||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \\. "$NVM_DIR/bash_completion"' \
|
|
||||||
| tee -a /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc >> /root/.zshrc \
|
|
||||||
&& echo "if [ \"\$(stat -c '%U' ${NVM_DIR})\" != \"${USERNAME}\" ]; then sudo chown -R ${USER_UID}:root ${NVM_DIR}; fi" \
|
|
||||||
| tee -a /root/.bashrc /root/.zshrc /home/${USERNAME}/.bashrc >> /home/${USERNAME}/.zshrc \
|
|
||||||
&& chown ${USER_UID}:${USER_GID} /home/${USERNAME}/.bashrc /home/${USERNAME}/.zshrc \
|
|
||||||
&& chown -R ${USER_UID}:root ${NVM_DIR} \
|
|
||||||
#
|
|
||||||
# Install yarn
|
|
||||||
&& curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
|
|
||||||
&& echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
|
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get -y install --no-install-recommends yarn; \
|
|
||||||
fi \
|
|
||||||
#
|
|
||||||
# [Optional] Install the Azure CLI
|
|
||||||
&& if [ "$INSTALL_AZURE_CLI" = "true" ]; then \
|
|
||||||
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/azure-cli.list \
|
|
||||||
&& curl -sL https://packages.microsoft.com/keys/microsoft.asc | apt-key add - 2>/dev/null \
|
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y azure-cli; \
|
|
||||||
fi \
|
|
||||||
#
|
|
||||||
# Install EF Core dotnet tool
|
|
||||||
&& dotnet tool install dotnet-ef --tool-path /home/$USERNAME/.dotnet/tools \
|
|
||||||
&& chown -R $USERNAME /home/$USERNAME/.dotnet \
|
|
||||||
#
|
|
||||||
# Clean up
|
|
||||||
&& apt-get autoremove -y \
|
|
||||||
&& apt-get clean -y \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Set PATH for dotnet tools
|
|
||||||
ENV PATH "$PATH:/home/$USERNAME/.dotnet/tools"
|
|
||||||
@@ -1,34 +1,12 @@
|
|||||||
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.112.0/containers/dotnetcore-3.1
|
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
|
||||||
{
|
{
|
||||||
"name": "eShopOnWeb",
|
"name": "eShopOnWeb",
|
||||||
"build": {
|
"image": "mcr.microsoft.com/devcontainers/dotnet:0-7.0",
|
||||||
"dockerfile": "Dockerfile",
|
|
||||||
"args": {
|
|
||||||
"USERNAME": "vscode",
|
|
||||||
"INSTALL_NODE": "false",
|
|
||||||
"NODE_VERSION": "lts/*",
|
|
||||||
"INSTALL_AZURE_CLI": "false"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Comment out to connect as root user. See https://aka.ms/vscode-remote/containers/non-root.
|
|
||||||
// make sure this is the same as USERNAME above
|
|
||||||
"remoteUser": "vscode",
|
|
||||||
"runArgs": [
|
|
||||||
"-v",
|
|
||||||
"/var/run/docker.sock:/var/run/docker.sock"
|
|
||||||
],
|
|
||||||
// Set *default* container specific settings.json values on container create.
|
|
||||||
"settings": {
|
|
||||||
"terminal.integrated.profiles.linux": {
|
|
||||||
"bash": {
|
|
||||||
"path": "bash",
|
|
||||||
"icon": "terminal-bash"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
|
"customizations": {
|
||||||
|
// Configure properties specific to VS Code.
|
||||||
|
"vscode": {
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"ms-dotnettools.csharp",
|
"ms-dotnettools.csharp",
|
||||||
@@ -36,12 +14,16 @@
|
|||||||
"ms-vscode.vscode-node-azure-pack",
|
"ms-vscode.vscode-node-azure-pack",
|
||||||
"ms-kubernetes-tools.vscode-kubernetes-tools",
|
"ms-kubernetes-tools.vscode-kubernetes-tools",
|
||||||
"redhat.vscode-yaml"
|
"redhat.vscode-yaml"
|
||||||
],
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
"forwardPorts": [5000, 5001],
|
"forwardPorts": [5000, 5001],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
"postCreateCommand": "dotnet dev-certs https --trust"
|
||||||
|
|
||||||
// [Optional] To reuse of your local HTTPS dev cert, first export it locally using this command:
|
// [Optional] To reuse of your local HTTPS dev cert, first export it locally using this command:
|
||||||
// * Windows PowerShell:
|
// * Windows PowerShell:
|
||||||
// dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere"
|
// dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere"
|
||||||
@@ -51,14 +33,11 @@
|
|||||||
// Next, after running the command above, uncomment lines in the 'mounts' and 'remoteEnv' lines below,
|
// Next, after running the command above, uncomment lines in the 'mounts' and 'remoteEnv' lines below,
|
||||||
// and open / rebuild the container so the settings take effect.
|
// and open / rebuild the container so the settings take effect.
|
||||||
//
|
//
|
||||||
"mounts": [
|
// "mounts": [
|
||||||
// "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind"
|
// // "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind"
|
||||||
],
|
// ],
|
||||||
"remoteEnv": {
|
// "remoteEnv": {
|
||||||
// "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere",
|
// // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere",
|
||||||
// "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx",
|
// // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx",
|
||||||
}
|
// },
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
|
||||||
// "postCreateCommand": "dotnet restore"
|
|
||||||
}
|
}
|
||||||
|
|||||||
36
.devcontainer/devcontainerreadme.md
Normal file
36
.devcontainer/devcontainerreadme.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Dev container
|
||||||
|
|
||||||
|
This project includes a [dev container](https://containers.dev/), which lets you use a container as a full-featured dev environment.
|
||||||
|
|
||||||
|
You can use the dev container configuration in this folder to build and run the app without needing to install any of its tools locally! You can use it in [GitHub Codespaces](https://github.com/features/codespaces) or the [VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
|
||||||
|
|
||||||
|
## GitHub Codespaces
|
||||||
|
Follow these steps to open this sample in a Codespace:
|
||||||
|
1. Click the **Code** drop-down menu at the top of https://github.com/dotnet-architecture/eShopOnWeb.
|
||||||
|
1. Click on the **Codespaces** tab.
|
||||||
|
1. Click **Create codespace on main** .
|
||||||
|
|
||||||
|
For more info, check out the [GitHub documentation](https://docs.github.com/en/free-pro-team@latest/github/developing-online-with-codespaces/creating-a-codespace#creating-a-codespace).
|
||||||
|
|
||||||
|
## VS Code Dev Containers
|
||||||
|
|
||||||
|
If you already have VS Code and Docker installed, you can click [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/dotnet-architecture/eShopOnWeb) to get started. This will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use.
|
||||||
|
|
||||||
|
You can also follow these steps to open this sample in a container using the VS Code Dev Containers extension:
|
||||||
|
|
||||||
|
1. If this is your first time using a development container, please ensure your system meets the pre-reqs (i.e. have Docker installed) in the [getting started steps](https://aka.ms/vscode-remote/containers/getting-started).
|
||||||
|
|
||||||
|
2. Open a locally cloned copy of the code:
|
||||||
|
|
||||||
|
- Clone this repository to your local filesystem.
|
||||||
|
- Press <kbd>F1</kbd> and select the **Dev Containers: Open Folder in Container...** command.
|
||||||
|
- Select the cloned copy of this folder, wait for the container to start, and try things out!
|
||||||
|
|
||||||
|
You can learn more in the [Dev Containers documentation](https://code.visualstudio.com/docs/devcontainers/containers).
|
||||||
|
|
||||||
|
## Tips and tricks
|
||||||
|
|
||||||
|
* Since the dev container is Linux-based, you won't be able to use LocalDB. Add ` "UseOnlyInMemoryDatabase": true,` to the [appsettings.json](../src/Web/appsettings.json) file (there's additional context on this [in the app's readme](../README.md#configuring-the-sample-to-use-sql-server)).
|
||||||
|
* If you get a `502 bad gateway` error, you may need to set your port to the https protocol. You can do this by opening the Ports view in VS Code (**Ports: Focus on Ports View**), right-clicking on the port you're using, select **Change Port Protocol**, and set **https**.
|
||||||
|
* If you are working with the same repository folder in a container and Windows, you'll want consistent line endings (otherwise you may see hundreds of changes in the SCM view). The `.gitattributes` file in the root of this repo disables line ending conversion and should prevent this. See [tips and tricks](https://code.visualstudio.com/docs/devcontainers/tips-and-tricks#_resolving-git-line-ending-issues-in-containers-resulting-in-many-modified-files) for more info.
|
||||||
|
* If you'd like to review the contents of the image used in this dev container, you can check it out in the [devcontainers/images](https://github.com/devcontainers/images/tree/main/src/dotnet) repo.
|
||||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
|
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||||
|
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||||
10
.github/workflows/richnav.yml
vendored
10
.github/workflows/richnav.yml
vendored
@@ -1,11 +1,11 @@
|
|||||||
name: eShopOnWeb Rich Code Navigation
|
name: eShopOnWeb - Code Index
|
||||||
|
|
||||||
on: [push, pull_request, workflow_dispatch]
|
on: workflow_dispatch
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: windows-2019
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -15,8 +15,10 @@ jobs:
|
|||||||
dotnet-version: 7.0.x
|
dotnet-version: 7.0.x
|
||||||
|
|
||||||
- name: Build with dotnet
|
- name: Build with dotnet
|
||||||
run: dotnet build ./eShopOnWeb.sln --configuration Release
|
run: dotnet build ./Everything.sln --configuration Release /bl
|
||||||
|
|
||||||
- uses: microsoft/RichCodeNavIndexer@v0.1
|
- uses: microsoft/RichCodeNavIndexer@v0.1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ github.token }}
|
repo-token: ${{ github.token }}
|
||||||
|
languages: 'csharp'
|
||||||
|
environment: 'internal'
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -257,3 +257,6 @@ pub/
|
|||||||
|
|
||||||
#Ignore marker-file used to know which docker files we have.
|
#Ignore marker-file used to know which docker files we have.
|
||||||
.eshopdocker_*
|
.eshopdocker_*
|
||||||
|
.devcontainer
|
||||||
|
|
||||||
|
.azure
|
||||||
|
|||||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -4,6 +4,7 @@
|
|||||||
"formulahendry.dotnet-test-explorer",
|
"formulahendry.dotnet-test-explorer",
|
||||||
"ms-vscode.vscode-node-azure-pack",
|
"ms-vscode.vscode-node-azure-pack",
|
||||||
"ms-kubernetes-tools.vscode-kubernetes-tools",
|
"ms-kubernetes-tools.vscode-kubernetes-tools",
|
||||||
"redhat.vscode-yaml"
|
"redhat.vscode-yaml",
|
||||||
|
"ms-azuretools.azure-dev"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,40 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Ardalis.ApiEndpoints" Version="4.0.1" />
|
<PackageVersion Include="Ardalis.ApiEndpoints" Version="4.0.1" />
|
||||||
<PackageVersion Include="Ardalis.GuardClauses" Version="4.0.1" />
|
<PackageVersion Include="Ardalis.GuardClauses" Version="4.0.1" />
|
||||||
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="6.1.0" />
|
<PackageVersion Include="Ardalis.Specification.EntityFrameworkCore" Version="7.0.0" />
|
||||||
<PackageVersion Include="Ardalis.Result" Version="7.0.0" />
|
<PackageVersion Include="Ardalis.Result" Version="7.0.0" />
|
||||||
<PackageVersion Include="Ardalis.Specification" Version="6.1.0" />
|
<PackageVersion Include="Ardalis.Specification" Version="7.0.0" />
|
||||||
<PackageVersion Include="Ardalis.ListStartupServices" Version="1.1.4" />
|
<PackageVersion Include="Ardalis.ListStartupServices" Version="1.1.4" />
|
||||||
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
|
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.2.1" />
|
||||||
|
<PackageVersion Include="Azure.Identity" Version="1.9.0-beta.2" />
|
||||||
|
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||||
<PackageVersion Include="BlazorInputFile" Version="0.2.0" />
|
<PackageVersion Include="BlazorInputFile" Version="0.2.0" />
|
||||||
<PackageVersion Include="Blazored.LocalStorage" Version="4.3.0" />
|
<PackageVersion Include="Blazored.LocalStorage" Version="4.3.0" />
|
||||||
<PackageVersion Include="BuildBundlerMinifier" Version="3.2.449" PrivateAssets="All" />
|
<PackageVersion Include="BuildBundlerMinifier" Version="3.2.449" PrivateAssets="All" />
|
||||||
<PackageVersion Include="FluentValidation" Version="11.5.1" />
|
<PackageVersion Include="FluentValidation" Version="11.7.1" />
|
||||||
<PackageVersion Include="MediatR" Version="12.0.1" />
|
<PackageVersion Include="MediatR" Version="12.0.1" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.10" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.8" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="7.0.10" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.4" PrivateAssets="all" />
|
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.5" PrivateAssets="all" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.5" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.5" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.8" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.8" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.5" />
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
<PackageVersion Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.Extensions.Identity.Core" Version="7.0.5" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="7.0.0" />
|
<PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="7.0.0" />
|
||||||
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.5" />
|
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.6" />
|
||||||
<PackageVersion Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
<PackageVersion Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.5" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.5" />
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.4">
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.8">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageVersion>
|
</PackageVersion>
|
||||||
@@ -42,13 +43,13 @@
|
|||||||
<PackageVersion Include="System.Net.Http.Json" Version="7.0.1" />
|
<PackageVersion Include="System.Net.Http.Json" Version="7.0.1" />
|
||||||
<PackageVersion Include="System.Security.Claims" Version="4.3.0" />
|
<PackageVersion Include="System.Security.Claims" Version="4.3.0" />
|
||||||
<PackageVersion Include="System.Text.Json" Version="7.0.2" />
|
<PackageVersion Include="System.Text.Json" Version="7.0.2" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.32.3" />
|
||||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
||||||
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
||||||
<!-- Test -->
|
<!-- Test -->
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.4" />
|
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.8" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
|
||||||
<PackageVersion Include="xunit" Version="2.4.2" />
|
<PackageVersion Include="xunit" Version="2.4.2" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@@ -61,6 +62,6 @@
|
|||||||
<PackageVersion Include="Moq" Version="4.18.4" />
|
<PackageVersion Include="Moq" Version="4.18.4" />
|
||||||
<PackageVersion Include="MSTest.TestAdapter" Version="3.0.2" />
|
<PackageVersion Include="MSTest.TestAdapter" Version="3.0.2" />
|
||||||
<PackageVersion Include="MSTest.TestFramework" Version="3.0.2" />
|
<PackageVersion Include="MSTest.TestFramework" Version="3.0.2" />
|
||||||
<PackageVersion Include="coverlet.collector" Version="3.2.0" />
|
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
56
README.md
56
README.md
@@ -41,12 +41,55 @@ The goal for this sample is to demonstrate some of the principles and patterns d
|
|||||||
- Development Process for Azure-Hosted ASP.NET Core Apps
|
- Development Process for Azure-Hosted ASP.NET Core Apps
|
||||||
- Azure Hosting Recommendations for ASP.NET Core Web Apps
|
- Azure Hosting Recommendations for ASP.NET Core Web Apps
|
||||||
|
|
||||||
## Running the sample
|
## Running the sample using Azd template
|
||||||
|
|
||||||
The store's home page should look like this:
|
The store's home page should look like this:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
The Azure Developer CLI (`azd`) is a developer-centric command-line interface (CLI) tool for creating Azure applications.
|
||||||
|
|
||||||
|
You need to install it before running and deploying with Azure Developer CLI.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux/MacOS
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -fsSL https://aka.ms/install-azd.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
And you can also install with package managers, like winget, choco, and brew. For more details, you can follow the documentation: https://aka.ms/azure-dev/install.
|
||||||
|
|
||||||
|
After logging in with the following command, you will be able to use the azd cli to quickly provision and deploy the application.
|
||||||
|
|
||||||
|
```
|
||||||
|
azd auth login
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, execute the `azd init` command to initialize the environment.
|
||||||
|
```
|
||||||
|
azd init -t dotnet-architecture/eShopOnWeb
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `azd up` to provision all the resources to Azure and deploy the code to those resources.
|
||||||
|
```
|
||||||
|
azd up
|
||||||
|
```
|
||||||
|
|
||||||
|
According to the prompt, enter an `env name`, and select `subscription` and `location`, these are the necessary parameters when you create resources. Wait a moment for the resource deployment to complete, click the web endpoint and you will see the home page.
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
1. Considering security, we store its related data (id, password) in the **Azure Key Vault** when we create the database, and obtain it from the Key Vault when we use it. This is different from directly deploying applications locally.
|
||||||
|
2. The resource group name created in azure portal will be **rg-{env name}**.
|
||||||
|
|
||||||
|
You can also run the sample directly locally (See below).
|
||||||
|
|
||||||
|
## Running the sample locally
|
||||||
Most of the site's functionality works with just the web application running. However, the site's Admin page relies on Blazor WebAssembly running in the browser, and it must communicate with the server using the site's PublicApi web application. You'll need to also run this project. You can configure Visual Studio to start multiple projects, or just go to the PublicApi folder in a terminal window and run `dotnet run` from there. After that from the Web folder you should run `dotnet run --launch-profile Web`. Now you should be able to browse to `https://localhost:5001/`. The admin part in Blazor is accessible to `https://localhost:5001/admin`
|
Most of the site's functionality works with just the web application running. However, the site's Admin page relies on Blazor WebAssembly running in the browser, and it must communicate with the server using the site's PublicApi web application. You'll need to also run this project. You can configure Visual Studio to start multiple projects, or just go to the PublicApi folder in a terminal window and run `dotnet run` from there. After that from the Web folder you should run `dotnet run --launch-profile Web`. Now you should be able to browse to `https://localhost:5001/`. The admin part in Blazor is accessible to `https://localhost:5001/admin`
|
||||||
|
|
||||||
Note that if you use this approach, you'll need to stop the application manually in order to build the solution (otherwise you'll get file locking errors).
|
Note that if you use this approach, you'll need to stop the application manually in order to build the solution (otherwise you'll get file locking errors).
|
||||||
@@ -58,13 +101,12 @@ You can also run the samples in Docker (see below).
|
|||||||
|
|
||||||
### Configuring the sample to use SQL Server
|
### Configuring the sample to use SQL Server
|
||||||
|
|
||||||
1. By default, the project uses a real database. If you want an in memory database, you can add in `appsettings.json`
|
1. By default, the project uses a real database. If you want an in memory database, you can add in the `appsettings.json` file in the Web folder
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"UseOnlyInMemoryDatabase": true
|
"UseOnlyInMemoryDatabase": true
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Ensure your connection strings in `appsettings.json` point to a local SQL Server instance.
|
1. Ensure your connection strings in `appsettings.json` point to a local SQL Server instance.
|
||||||
@@ -98,6 +140,14 @@ You can also run the samples in Docker (see below).
|
|||||||
dotnet ef migrations add InitialIdentityModel --context appidentitydbcontext -p ../Infrastructure/Infrastructure.csproj -s Web.csproj -o Identity/Migrations
|
dotnet ef migrations add InitialIdentityModel --context appidentitydbcontext -p ../Infrastructure/Infrastructure.csproj -s Web.csproj -o Identity/Migrations
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Running the sample in the dev container
|
||||||
|
|
||||||
|
This project includes a `.devcontainer` folder with a [dev container configuration](https://containers.dev/), which lets you use a container as a full-featured dev environment.
|
||||||
|
|
||||||
|
You can use the dev container to build and run the app without needing to install any of its tools locally! You can work in GitHub Codespaces or the VS Code Dev Containers extension.
|
||||||
|
|
||||||
|
Learn more about using the dev container in its [readme](/.devcontainer/devcontainerreadme.md).
|
||||||
|
|
||||||
## Running the sample using Docker
|
## Running the sample using Docker
|
||||||
|
|
||||||
You can run the Web sample by running these commands from the root folder (where the .sln file is located):
|
You can run the Web sample by running these commands from the root folder (where the .sln file is located):
|
||||||
|
|||||||
8
azure.yaml
Normal file
8
azure.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/wbreza/azure-dev/main/schemas/v1.0/azure.yaml.json
|
||||||
|
|
||||||
|
name: eShopOnWeb
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
project: ./src/Web
|
||||||
|
language: csharp
|
||||||
|
host: appservice
|
||||||
135
infra/abbreviations.json
Normal file
135
infra/abbreviations.json
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
{
|
||||||
|
"analysisServicesServers": "as",
|
||||||
|
"apiManagementService": "apim-",
|
||||||
|
"appConfigurationConfigurationStores": "appcs-",
|
||||||
|
"appManagedEnvironments": "cae-",
|
||||||
|
"appContainerApps": "ca-",
|
||||||
|
"authorizationPolicyDefinitions": "policy-",
|
||||||
|
"automationAutomationAccounts": "aa-",
|
||||||
|
"blueprintBlueprints": "bp-",
|
||||||
|
"blueprintBlueprintsArtifacts": "bpa-",
|
||||||
|
"cacheRedis": "redis-",
|
||||||
|
"cdnProfiles": "cdnp-",
|
||||||
|
"cdnProfilesEndpoints": "cdne-",
|
||||||
|
"cognitiveServicesAccounts": "cog-",
|
||||||
|
"cognitiveServicesFormRecognizer": "cog-fr-",
|
||||||
|
"cognitiveServicesTextAnalytics": "cog-ta-",
|
||||||
|
"computeAvailabilitySets": "avail-",
|
||||||
|
"computeCloudServices": "cld-",
|
||||||
|
"computeDiskEncryptionSets": "des",
|
||||||
|
"computeDisks": "disk",
|
||||||
|
"computeDisksOs": "osdisk",
|
||||||
|
"computeGalleries": "gal",
|
||||||
|
"computeSnapshots": "snap-",
|
||||||
|
"computeVirtualMachines": "vm",
|
||||||
|
"computeVirtualMachineScaleSets": "vmss-",
|
||||||
|
"containerInstanceContainerGroups": "ci",
|
||||||
|
"containerRegistryRegistries": "cr",
|
||||||
|
"containerServiceManagedClusters": "aks-",
|
||||||
|
"databricksWorkspaces": "dbw-",
|
||||||
|
"dataFactoryFactories": "adf-",
|
||||||
|
"dataLakeAnalyticsAccounts": "dla",
|
||||||
|
"dataLakeStoreAccounts": "dls",
|
||||||
|
"dataMigrationServices": "dms-",
|
||||||
|
"dBforMySQLServers": "mysql-",
|
||||||
|
"dBforPostgreSQLServers": "psql-",
|
||||||
|
"devicesIotHubs": "iot-",
|
||||||
|
"devicesProvisioningServices": "provs-",
|
||||||
|
"devicesProvisioningServicesCertificates": "pcert-",
|
||||||
|
"documentDBDatabaseAccounts": "cosmos-",
|
||||||
|
"eventGridDomains": "evgd-",
|
||||||
|
"eventGridDomainsTopics": "evgt-",
|
||||||
|
"eventGridEventSubscriptions": "evgs-",
|
||||||
|
"eventHubNamespaces": "evhns-",
|
||||||
|
"eventHubNamespacesEventHubs": "evh-",
|
||||||
|
"hdInsightClustersHadoop": "hadoop-",
|
||||||
|
"hdInsightClustersHbase": "hbase-",
|
||||||
|
"hdInsightClustersKafka": "kafka-",
|
||||||
|
"hdInsightClustersMl": "mls-",
|
||||||
|
"hdInsightClustersSpark": "spark-",
|
||||||
|
"hdInsightClustersStorm": "storm-",
|
||||||
|
"hybridComputeMachines": "arcs-",
|
||||||
|
"insightsActionGroups": "ag-",
|
||||||
|
"insightsComponents": "appi-",
|
||||||
|
"keyVaultVaults": "kv-",
|
||||||
|
"kubernetesConnectedClusters": "arck",
|
||||||
|
"kustoClusters": "dec",
|
||||||
|
"kustoClustersDatabases": "dedb",
|
||||||
|
"logicIntegrationAccounts": "ia-",
|
||||||
|
"logicWorkflows": "logic-",
|
||||||
|
"machineLearningServicesWorkspaces": "mlw-",
|
||||||
|
"managedIdentityUserAssignedIdentities": "id-",
|
||||||
|
"managementManagementGroups": "mg-",
|
||||||
|
"migrateAssessmentProjects": "migr-",
|
||||||
|
"networkApplicationGateways": "agw-",
|
||||||
|
"networkApplicationSecurityGroups": "asg-",
|
||||||
|
"networkAzureFirewalls": "afw-",
|
||||||
|
"networkBastionHosts": "bas-",
|
||||||
|
"networkConnections": "con-",
|
||||||
|
"networkDnsZones": "dnsz-",
|
||||||
|
"networkExpressRouteCircuits": "erc-",
|
||||||
|
"networkFirewallPolicies": "afwp-",
|
||||||
|
"networkFirewallPoliciesWebApplication": "waf",
|
||||||
|
"networkFirewallPoliciesRuleGroups": "wafrg",
|
||||||
|
"networkFrontDoors": "fd-",
|
||||||
|
"networkFrontdoorWebApplicationFirewallPolicies": "fdfp-",
|
||||||
|
"networkLoadBalancersExternal": "lbe-",
|
||||||
|
"networkLoadBalancersInternal": "lbi-",
|
||||||
|
"networkLoadBalancersInboundNatRules": "rule-",
|
||||||
|
"networkLocalNetworkGateways": "lgw-",
|
||||||
|
"networkNatGateways": "ng-",
|
||||||
|
"networkNetworkInterfaces": "nic-",
|
||||||
|
"networkNetworkSecurityGroups": "nsg-",
|
||||||
|
"networkNetworkSecurityGroupsSecurityRules": "nsgsr-",
|
||||||
|
"networkNetworkWatchers": "nw-",
|
||||||
|
"networkPrivateDnsZones": "pdnsz-",
|
||||||
|
"networkPrivateLinkServices": "pl-",
|
||||||
|
"networkPublicIPAddresses": "pip-",
|
||||||
|
"networkPublicIPPrefixes": "ippre-",
|
||||||
|
"networkRouteFilters": "rf-",
|
||||||
|
"networkRouteTables": "rt-",
|
||||||
|
"networkRouteTablesRoutes": "udr-",
|
||||||
|
"networkTrafficManagerProfiles": "traf-",
|
||||||
|
"networkVirtualNetworkGateways": "vgw-",
|
||||||
|
"networkVirtualNetworks": "vnet-",
|
||||||
|
"networkVirtualNetworksSubnets": "snet-",
|
||||||
|
"networkVirtualNetworksVirtualNetworkPeerings": "peer-",
|
||||||
|
"networkVirtualWans": "vwan-",
|
||||||
|
"networkVpnGateways": "vpng-",
|
||||||
|
"networkVpnGatewaysVpnConnections": "vcn-",
|
||||||
|
"networkVpnGatewaysVpnSites": "vst-",
|
||||||
|
"notificationHubsNamespaces": "ntfns-",
|
||||||
|
"notificationHubsNamespacesNotificationHubs": "ntf-",
|
||||||
|
"operationalInsightsWorkspaces": "log-",
|
||||||
|
"portalDashboards": "dash-",
|
||||||
|
"powerBIDedicatedCapacities": "pbi-",
|
||||||
|
"purviewAccounts": "pview-",
|
||||||
|
"recoveryServicesVaults": "rsv-",
|
||||||
|
"resourcesResourceGroups": "rg-",
|
||||||
|
"searchSearchServices": "srch-",
|
||||||
|
"serviceBusNamespaces": "sb-",
|
||||||
|
"serviceBusNamespacesQueues": "sbq-",
|
||||||
|
"serviceBusNamespacesTopics": "sbt-",
|
||||||
|
"serviceEndPointPolicies": "se-",
|
||||||
|
"serviceFabricClusters": "sf-",
|
||||||
|
"signalRServiceSignalR": "sigr",
|
||||||
|
"sqlManagedInstances": "sqlmi-",
|
||||||
|
"sqlServers": "sql-",
|
||||||
|
"sqlServersDataWarehouse": "sqldw-",
|
||||||
|
"sqlServersDatabases": "sqldb-",
|
||||||
|
"sqlServersDatabasesStretch": "sqlstrdb-",
|
||||||
|
"storageStorageAccounts": "st",
|
||||||
|
"storageStorageAccountsVm": "stvm",
|
||||||
|
"storSimpleManagers": "ssimp",
|
||||||
|
"streamAnalyticsCluster": "asa-",
|
||||||
|
"synapseWorkspaces": "syn",
|
||||||
|
"synapseWorkspacesAnalyticsWorkspaces": "synw",
|
||||||
|
"synapseWorkspacesSqlPoolsDedicated": "syndp",
|
||||||
|
"synapseWorkspacesSqlPoolsSpark": "synsp",
|
||||||
|
"timeSeriesInsightsEnvironments": "tsi-",
|
||||||
|
"webServerFarms": "plan-",
|
||||||
|
"webSitesAppService": "app-",
|
||||||
|
"webSitesAppServiceEnvironment": "ase-",
|
||||||
|
"webSitesFunctions": "func-",
|
||||||
|
"webStaticSites": "stapp-"
|
||||||
|
}
|
||||||
129
infra/core/database/sqlserver/sqlserver.bicep
Normal file
129
infra/core/database/sqlserver/sqlserver.bicep
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
param name string
|
||||||
|
param location string = resourceGroup().location
|
||||||
|
param tags object = {}
|
||||||
|
|
||||||
|
param appUser string = 'appUser'
|
||||||
|
param databaseName string
|
||||||
|
param keyVaultName string
|
||||||
|
param sqlAdmin string = 'sqlAdmin'
|
||||||
|
param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING'
|
||||||
|
|
||||||
|
@secure()
|
||||||
|
param sqlAdminPassword string
|
||||||
|
@secure()
|
||||||
|
param appUserPassword string
|
||||||
|
|
||||||
|
resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
|
||||||
|
name: name
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
properties: {
|
||||||
|
version: '12.0'
|
||||||
|
minimalTlsVersion: '1.2'
|
||||||
|
publicNetworkAccess: 'Enabled'
|
||||||
|
administratorLogin: sqlAdmin
|
||||||
|
administratorLoginPassword: sqlAdminPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
resource database 'databases' = {
|
||||||
|
name: databaseName
|
||||||
|
location: location
|
||||||
|
}
|
||||||
|
|
||||||
|
resource firewall 'firewallRules' = {
|
||||||
|
name: 'Azure Services'
|
||||||
|
properties: {
|
||||||
|
// Allow all clients
|
||||||
|
// Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only".
|
||||||
|
// This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes.
|
||||||
|
startIpAddress: '0.0.0.1'
|
||||||
|
endIpAddress: '255.255.255.254'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
|
||||||
|
name: '${name}-deployment-script'
|
||||||
|
location: location
|
||||||
|
kind: 'AzureCLI'
|
||||||
|
properties: {
|
||||||
|
azCliVersion: '2.37.0'
|
||||||
|
retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running
|
||||||
|
timeout: 'PT5M' // Five minutes
|
||||||
|
cleanupPreference: 'OnSuccess'
|
||||||
|
environmentVariables: [
|
||||||
|
{
|
||||||
|
name: 'APPUSERNAME'
|
||||||
|
value: appUser
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'APPUSERPASSWORD'
|
||||||
|
secureValue: appUserPassword
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'DBNAME'
|
||||||
|
value: databaseName
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'DBSERVER'
|
||||||
|
value: sqlServer.properties.fullyQualifiedDomainName
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'SQLCMDPASSWORD'
|
||||||
|
secureValue: sqlAdminPassword
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'SQLADMIN'
|
||||||
|
value: sqlAdmin
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
scriptContent: '''
|
||||||
|
wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2
|
||||||
|
tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C .
|
||||||
|
|
||||||
|
cat <<SCRIPT_END > ./initDb.sql
|
||||||
|
drop user ${APPUSERNAME}
|
||||||
|
go
|
||||||
|
create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}'
|
||||||
|
go
|
||||||
|
alter role db_owner add member ${APPUSERNAME}
|
||||||
|
go
|
||||||
|
SCRIPT_END
|
||||||
|
|
||||||
|
./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
|
||||||
|
parent: keyVault
|
||||||
|
name: 'sqlAdminPassword'
|
||||||
|
properties: {
|
||||||
|
value: sqlAdminPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
|
||||||
|
parent: keyVault
|
||||||
|
name: 'appUserPassword'
|
||||||
|
properties: {
|
||||||
|
value: appUserPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
|
||||||
|
parent: keyVault
|
||||||
|
name: connectionStringKey
|
||||||
|
properties: {
|
||||||
|
value: '${connectionString}; Password=${appUserPassword}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
|
||||||
|
name: keyVaultName
|
||||||
|
}
|
||||||
|
|
||||||
|
var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}'
|
||||||
|
output connectionStringKey string = connectionStringKey
|
||||||
|
output databaseName string = sqlServer::database.name
|
||||||
101
infra/core/host/appservice.bicep
Normal file
101
infra/core/host/appservice.bicep
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
param name string
|
||||||
|
param location string = resourceGroup().location
|
||||||
|
param tags object = {}
|
||||||
|
|
||||||
|
// Reference Properties
|
||||||
|
param applicationInsightsName string = ''
|
||||||
|
param appServicePlanId string
|
||||||
|
param keyVaultName string = ''
|
||||||
|
param managedIdentity bool = !empty(keyVaultName)
|
||||||
|
|
||||||
|
// Runtime Properties
|
||||||
|
@allowed([
|
||||||
|
'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom'
|
||||||
|
])
|
||||||
|
param runtimeName string
|
||||||
|
param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}'
|
||||||
|
param runtimeVersion string
|
||||||
|
|
||||||
|
// Microsoft.Web/sites Properties
|
||||||
|
param kind string = 'app,linux'
|
||||||
|
|
||||||
|
// Microsoft.Web/sites/config
|
||||||
|
param allowedOrigins array = []
|
||||||
|
param alwaysOn bool = true
|
||||||
|
param appCommandLine string = ''
|
||||||
|
param appSettings object = {}
|
||||||
|
param clientAffinityEnabled bool = false
|
||||||
|
param enableOryxBuild bool = contains(kind, 'linux')
|
||||||
|
param functionAppScaleLimit int = -1
|
||||||
|
param linuxFxVersion string = runtimeNameAndVersion
|
||||||
|
param minimumElasticInstanceCount int = -1
|
||||||
|
param numberOfWorkers int = -1
|
||||||
|
param scmDoBuildDuringDeployment bool = false
|
||||||
|
param use32BitWorkerProcess bool = false
|
||||||
|
param ftpsState string = 'FtpsOnly'
|
||||||
|
param healthCheckPath string = ''
|
||||||
|
|
||||||
|
resource appService 'Microsoft.Web/sites@2022-03-01' = {
|
||||||
|
name: name
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
kind: kind
|
||||||
|
properties: {
|
||||||
|
serverFarmId: appServicePlanId
|
||||||
|
siteConfig: {
|
||||||
|
linuxFxVersion: linuxFxVersion
|
||||||
|
alwaysOn: alwaysOn
|
||||||
|
ftpsState: ftpsState
|
||||||
|
minTlsVersion: '1.2'
|
||||||
|
appCommandLine: appCommandLine
|
||||||
|
numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null
|
||||||
|
minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null
|
||||||
|
use32BitWorkerProcess: use32BitWorkerProcess
|
||||||
|
functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null
|
||||||
|
healthCheckPath: healthCheckPath
|
||||||
|
cors: {
|
||||||
|
allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientAffinityEnabled: clientAffinityEnabled
|
||||||
|
httpsOnly: true
|
||||||
|
}
|
||||||
|
|
||||||
|
identity: { type: managedIdentity ? 'SystemAssigned' : 'None' }
|
||||||
|
|
||||||
|
resource configAppSettings 'config' = {
|
||||||
|
name: 'appsettings'
|
||||||
|
properties: union(appSettings,
|
||||||
|
{
|
||||||
|
SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment)
|
||||||
|
ENABLE_ORYX_BUILD: string(enableOryxBuild)
|
||||||
|
},
|
||||||
|
!empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {},
|
||||||
|
!empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource configLogs 'config' = {
|
||||||
|
name: 'logs'
|
||||||
|
properties: {
|
||||||
|
applicationLogs: { fileSystem: { level: 'Verbose' } }
|
||||||
|
detailedErrorMessages: { enabled: true }
|
||||||
|
failedRequestsTracing: { enabled: true }
|
||||||
|
httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } }
|
||||||
|
}
|
||||||
|
dependsOn: [
|
||||||
|
configAppSettings
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) {
|
||||||
|
name: keyVaultName
|
||||||
|
}
|
||||||
|
|
||||||
|
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) {
|
||||||
|
name: applicationInsightsName
|
||||||
|
}
|
||||||
|
|
||||||
|
output identityPrincipalId string = managedIdentity ? appService.identity.principalId : ''
|
||||||
|
output name string = appService.name
|
||||||
|
output uri string = 'https://${appService.properties.defaultHostName}'
|
||||||
20
infra/core/host/appserviceplan.bicep
Normal file
20
infra/core/host/appserviceplan.bicep
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
param name string
|
||||||
|
param location string = resourceGroup().location
|
||||||
|
param tags object = {}
|
||||||
|
|
||||||
|
param kind string = ''
|
||||||
|
param reserved bool = true
|
||||||
|
param sku object
|
||||||
|
|
||||||
|
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
|
||||||
|
name: name
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
sku: sku
|
||||||
|
kind: kind
|
||||||
|
properties: {
|
||||||
|
reserved: reserved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output id string = appServicePlan.id
|
||||||
21
infra/core/security/keyvault-access.bicep
Normal file
21
infra/core/security/keyvault-access.bicep
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
param name string = 'add'
|
||||||
|
|
||||||
|
param keyVaultName string
|
||||||
|
param permissions object = { secrets: [ 'get', 'list' ] }
|
||||||
|
param principalId string
|
||||||
|
|
||||||
|
resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = {
|
||||||
|
parent: keyVault
|
||||||
|
name: name
|
||||||
|
properties: {
|
||||||
|
accessPolicies: [ {
|
||||||
|
objectId: principalId
|
||||||
|
tenantId: subscription().tenantId
|
||||||
|
permissions: permissions
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
|
||||||
|
name: keyVaultName
|
||||||
|
}
|
||||||
25
infra/core/security/keyvault.bicep
Normal file
25
infra/core/security/keyvault.bicep
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
param name string
|
||||||
|
param location string = resourceGroup().location
|
||||||
|
param tags object = {}
|
||||||
|
|
||||||
|
param principalId string = ''
|
||||||
|
|
||||||
|
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
|
||||||
|
name: name
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
properties: {
|
||||||
|
tenantId: subscription().tenantId
|
||||||
|
sku: { family: 'A', name: 'standard' }
|
||||||
|
accessPolicies: !empty(principalId) ? [
|
||||||
|
{
|
||||||
|
objectId: principalId
|
||||||
|
permissions: { secrets: [ 'get', 'list' ] }
|
||||||
|
tenantId: subscription().tenantId
|
||||||
|
}
|
||||||
|
] : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output endpoint string = keyVault.properties.vaultUri
|
||||||
|
output name string = keyVault.name
|
||||||
144
infra/main.bicep
Normal file
144
infra/main.bicep
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
targetScope = 'subscription'
|
||||||
|
|
||||||
|
@minLength(1)
|
||||||
|
@maxLength(64)
|
||||||
|
@description('Name of the the environment which is used to generate a short unique hash used in all resources.')
|
||||||
|
param environmentName string
|
||||||
|
|
||||||
|
@minLength(1)
|
||||||
|
@description('Primary location for all resources')
|
||||||
|
param location string
|
||||||
|
|
||||||
|
// Optional parameters to override the default azd resource naming conventions. Update the main.parameters.json file to provide values. e.g.,:
|
||||||
|
// "resourceGroupName": {
|
||||||
|
// "value": "myGroupName"
|
||||||
|
// }
|
||||||
|
param resourceGroupName string = ''
|
||||||
|
param webServiceName string = ''
|
||||||
|
param catalogDatabaseName string = 'catalogDatabase'
|
||||||
|
param catalogDatabaseServerName string = ''
|
||||||
|
param identityDatabaseName string = 'identityDatabase'
|
||||||
|
param identityDatabaseServerName string = ''
|
||||||
|
param appServicePlanName string = ''
|
||||||
|
param keyVaultName string = ''
|
||||||
|
|
||||||
|
@description('Id of the user or app to assign application roles')
|
||||||
|
param principalId string = ''
|
||||||
|
|
||||||
|
@secure()
|
||||||
|
@description('SQL Server administrator password')
|
||||||
|
param sqlAdminPassword string
|
||||||
|
|
||||||
|
@secure()
|
||||||
|
@description('Application user password')
|
||||||
|
param appUserPassword string
|
||||||
|
|
||||||
|
var abbrs = loadJsonContent('./abbreviations.json')
|
||||||
|
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
|
||||||
|
var tags = { 'azd-env-name': environmentName }
|
||||||
|
|
||||||
|
// Organize resources in a resource group
|
||||||
|
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
|
||||||
|
name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}'
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
}
|
||||||
|
|
||||||
|
// The application frontend
|
||||||
|
module web './core/host/appservice.bicep' = {
|
||||||
|
name: 'web'
|
||||||
|
scope: rg
|
||||||
|
params: {
|
||||||
|
name: !empty(webServiceName) ? webServiceName : '${abbrs.webSitesAppService}web-${resourceToken}'
|
||||||
|
location: location
|
||||||
|
appServicePlanId: appServicePlan.outputs.id
|
||||||
|
keyVaultName: keyVault.outputs.name
|
||||||
|
runtimeName: 'dotnetcore'
|
||||||
|
runtimeVersion: '7.0'
|
||||||
|
tags: union(tags, { 'azd-service-name': 'web' })
|
||||||
|
appSettings: {
|
||||||
|
AZURE_SQL_CATALOG_CONNECTION_STRING_KEY: 'AZURE-SQL-CATALOG-CONNECTION-STRING'
|
||||||
|
AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY: 'AZURE-SQL-IDENTITY-CONNECTION-STRING'
|
||||||
|
AZURE_KEY_VAULT_ENDPOINT: keyVault.outputs.endpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module apiKeyVaultAccess './core/security/keyvault-access.bicep' = {
|
||||||
|
name: 'api-keyvault-access'
|
||||||
|
scope: rg
|
||||||
|
params: {
|
||||||
|
keyVaultName: keyVault.outputs.name
|
||||||
|
principalId: web.outputs.identityPrincipalId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The application database: Catalog
|
||||||
|
module catalogDb './core/database/sqlserver/sqlserver.bicep' = {
|
||||||
|
name: 'sql-catalog'
|
||||||
|
scope: rg
|
||||||
|
params: {
|
||||||
|
name: !empty(catalogDatabaseServerName) ? catalogDatabaseServerName : '${abbrs.sqlServers}catalog-${resourceToken}'
|
||||||
|
databaseName: catalogDatabaseName
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
sqlAdminPassword: sqlAdminPassword
|
||||||
|
appUserPassword: appUserPassword
|
||||||
|
keyVaultName: keyVault.outputs.name
|
||||||
|
connectionStringKey: 'AZURE-SQL-CATALOG-CONNECTION-STRING'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The application database: Identity
|
||||||
|
module identityDb './core/database/sqlserver/sqlserver.bicep' = {
|
||||||
|
name: 'sql-identity'
|
||||||
|
scope: rg
|
||||||
|
params: {
|
||||||
|
name: !empty(identityDatabaseServerName) ? identityDatabaseServerName : '${abbrs.sqlServers}identity-${resourceToken}'
|
||||||
|
databaseName: identityDatabaseName
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
sqlAdminPassword: sqlAdminPassword
|
||||||
|
appUserPassword: appUserPassword
|
||||||
|
keyVaultName: keyVault.outputs.name
|
||||||
|
connectionStringKey: 'AZURE-SQL-IDENTITY-CONNECTION-STRING'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store secrets in a keyvault
|
||||||
|
module keyVault './core/security/keyvault.bicep' = {
|
||||||
|
name: 'keyvault'
|
||||||
|
scope: rg
|
||||||
|
params: {
|
||||||
|
name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}'
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
principalId: principalId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an App Service Plan to group applications under the same payment plan and SKU
|
||||||
|
module appServicePlan './core/host/appserviceplan.bicep' = {
|
||||||
|
name: 'appserviceplan'
|
||||||
|
scope: rg
|
||||||
|
params: {
|
||||||
|
name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}'
|
||||||
|
location: location
|
||||||
|
tags: tags
|
||||||
|
sku: {
|
||||||
|
name: 'B1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data outputs
|
||||||
|
output AZURE_SQL_CATALOG_CONNECTION_STRING_KEY string = catalogDb.outputs.connectionStringKey
|
||||||
|
output AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY string = identityDb.outputs.connectionStringKey
|
||||||
|
output AZURE_SQL_CATALOG_DATABASE_NAME string = catalogDb.outputs.databaseName
|
||||||
|
output AZURE_SQL_IDENTITY_DATABASE_NAME string = identityDb.outputs.databaseName
|
||||||
|
|
||||||
|
// App outputs
|
||||||
|
output AZURE_LOCATION string = location
|
||||||
|
output AZURE_TENANT_ID string = tenant().tenantId
|
||||||
|
output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint
|
||||||
|
output AZURE_KEY_VAULT_NAME string = keyVault.outputs.name
|
||||||
21
infra/main.parameters.json
Normal file
21
infra/main.parameters.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
|
||||||
|
"contentVersion": "1.0.0.0",
|
||||||
|
"parameters": {
|
||||||
|
"environmentName": {
|
||||||
|
"value": "${AZURE_ENV_NAME}"
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"value": "${AZURE_LOCATION}"
|
||||||
|
},
|
||||||
|
"principalId": {
|
||||||
|
"value": "${AZURE_PRINCIPAL_ID}"
|
||||||
|
},
|
||||||
|
"sqlAdminPassword": {
|
||||||
|
"value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} sqlAdminPassword)"
|
||||||
|
},
|
||||||
|
"appUserPassword": {
|
||||||
|
"value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} appUserPassword)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Ardalis.Specification;
|
||||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.ApplicationCore.Specifications;
|
||||||
|
|
||||||
|
public class CustomerOrdersSpecification : Specification<Order>
|
||||||
|
{
|
||||||
|
public CustomerOrdersSpecification(string buyerId)
|
||||||
|
{
|
||||||
|
Query.Where(o => o.BuyerId == buyerId)
|
||||||
|
.Include(o => o.OrderItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"CatalogConnection": "Server=sqlserver,1433;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;Trusted_Connection=false;",
|
"CatalogConnection": "Server=sqlserver,1433;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.CatalogDb;User Id=sa;Password=@someThingComplicated1234;Trusted_Connection=false;TrustServerCertificate=true;",
|
||||||
"IdentityConnection": "Server=sqlserver,1433;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;Trusted_Connection=false;"
|
"IdentityConnection": "Server=sqlserver,1433;Integrated Security=true;Initial Catalog=Microsoft.eShopOnWeb.Identity;User Id=sa;Password=@someThingComplicated1234;Trusted_Connection=false;TrustServerCertificate=true;"
|
||||||
},
|
},
|
||||||
"baseUrls": {
|
"baseUrls": {
|
||||||
"apiBase": "http://localhost:5200/api/",
|
"apiBase": "http://localhost:5200/api/",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using MediatR;
|
||||||
using MediatR;
|
|
||||||
using Microsoft.eShopWeb.Web.ViewModels;
|
using Microsoft.eShopWeb.Web.ViewModels;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Web.Features.MyOrders;
|
namespace Microsoft.eShopWeb.Web.Features.MyOrders;
|
||||||
|
|||||||
@@ -18,20 +18,12 @@ public class GetMyOrdersHandler : IRequestHandler<GetMyOrders, IEnumerable<Order
|
|||||||
public async Task<IEnumerable<OrderViewModel>> Handle(GetMyOrders request,
|
public async Task<IEnumerable<OrderViewModel>> Handle(GetMyOrders request,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var specification = new CustomerOrdersWithItemsSpecification(request.UserName);
|
var specification = new CustomerOrdersSpecification(request.UserName);
|
||||||
var orders = await _orderRepository.ListAsync(specification, cancellationToken);
|
var orders = await _orderRepository.ListAsync(specification, cancellationToken);
|
||||||
|
|
||||||
return orders.Select(o => new OrderViewModel
|
return orders.Select(o => new OrderViewModel
|
||||||
{
|
{
|
||||||
OrderDate = o.OrderDate,
|
OrderDate = o.OrderDate,
|
||||||
OrderItems = o.OrderItems.Select(oi => new OrderItemViewModel()
|
|
||||||
{
|
|
||||||
PictureUrl = oi.ItemOrdered.PictureUri,
|
|
||||||
ProductId = oi.ItemOrdered.CatalogItemId,
|
|
||||||
ProductName = oi.ItemOrdered.ProductName,
|
|
||||||
UnitPrice = oi.UnitPrice,
|
|
||||||
Units = oi.Units
|
|
||||||
}).ToList(),
|
|
||||||
OrderNumber = o.Id,
|
OrderNumber = o.Id,
|
||||||
ShippingAddress = o.ShipToAddress,
|
ShippingAddress = o.ShipToAddress,
|
||||||
Total = o.Total()
|
Total = o.Total()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Microsoft.eShopWeb.Web.ViewModels;
|
|||||||
|
|
||||||
namespace Microsoft.eShopWeb.Web.Features.OrderDetails;
|
namespace Microsoft.eShopWeb.Web.Features.OrderDetails;
|
||||||
|
|
||||||
public class GetOrderDetails : IRequest<OrderViewModel>
|
public class GetOrderDetails : IRequest<OrderDetailViewModel>
|
||||||
{
|
{
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
public int OrderId { get; set; }
|
public int OrderId { get; set; }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using Microsoft.eShopWeb.Web.ViewModels;
|
|||||||
|
|
||||||
namespace Microsoft.eShopWeb.Web.Features.OrderDetails;
|
namespace Microsoft.eShopWeb.Web.Features.OrderDetails;
|
||||||
|
|
||||||
public class GetOrderDetailsHandler : IRequestHandler<GetOrderDetails, OrderViewModel?>
|
public class GetOrderDetailsHandler : IRequestHandler<GetOrderDetails, OrderDetailViewModel?>
|
||||||
{
|
{
|
||||||
private readonly IReadRepository<Order> _orderRepository;
|
private readonly IReadRepository<Order> _orderRepository;
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ public class GetOrderDetailsHandler : IRequestHandler<GetOrderDetails, OrderView
|
|||||||
_orderRepository = orderRepository;
|
_orderRepository = orderRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrderViewModel?> Handle(GetOrderDetails request,
|
public async Task<OrderDetailViewModel?> Handle(GetOrderDetails request,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var spec = new OrderWithItemsByIdSpec(request.OrderId);
|
var spec = new OrderWithItemsByIdSpec(request.OrderId);
|
||||||
@@ -26,7 +26,7 @@ public class GetOrderDetailsHandler : IRequestHandler<GetOrderDetails, OrderView
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OrderViewModel
|
return new OrderDetailViewModel
|
||||||
{
|
{
|
||||||
OrderDate = order.OrderDate,
|
OrderDate = order.OrderDate,
|
||||||
OrderItems = order.OrderItems.Select(oi => new OrderItemViewModel
|
OrderItems = order.OrderItems.Select(oi => new OrderItemViewModel
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using Ardalis.ListStartupServices;
|
using Ardalis.ListStartupServices;
|
||||||
|
using Azure.Identity;
|
||||||
using BlazorAdmin;
|
using BlazorAdmin;
|
||||||
using BlazorAdmin.Services;
|
using BlazorAdmin.Services;
|
||||||
using Blazored.LocalStorage;
|
using Blazored.LocalStorage;
|
||||||
@@ -8,6 +9,7 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
|||||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.eShopWeb;
|
using Microsoft.eShopWeb;
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
|
||||||
using Microsoft.eShopWeb.Infrastructure.Data;
|
using Microsoft.eShopWeb.Infrastructure.Data;
|
||||||
@@ -18,10 +20,27 @@ using Microsoft.eShopWeb.Web.HealthChecks;
|
|||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Logging.AddConsole();
|
builder.Logging.AddConsole();
|
||||||
|
|
||||||
|
if (builder.Environment.IsDevelopment() || builder.Environment.EnvironmentName == "Docker"){
|
||||||
|
// Configure SQL Server (local)
|
||||||
Microsoft.eShopWeb.Infrastructure.Dependencies.ConfigureServices(builder.Configuration, builder.Services);
|
Microsoft.eShopWeb.Infrastructure.Dependencies.ConfigureServices(builder.Configuration, builder.Services);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// Configure SQL Server (prod)
|
||||||
|
var credential = new ChainedTokenCredential(new AzureDeveloperCliCredential(), new DefaultAzureCredential());
|
||||||
|
builder.Configuration.AddAzureKeyVault(new Uri(builder.Configuration["AZURE_KEY_VAULT_ENDPOINT"] ?? ""), credential);
|
||||||
|
builder.Services.AddDbContext<CatalogContext>(c =>
|
||||||
|
{
|
||||||
|
var connectionString = builder.Configuration[builder.Configuration["AZURE_SQL_CATALOG_CONNECTION_STRING_KEY"] ?? ""];
|
||||||
|
c.UseSqlServer(connectionString, sqlOptions => sqlOptions.EnableRetryOnFailure());
|
||||||
|
});
|
||||||
|
builder.Services.AddDbContext<AppIdentityDbContext>(options =>
|
||||||
|
{
|
||||||
|
var connectionString = builder.Configuration[builder.Configuration["AZURE_SQL_IDENTITY_CONNECTION_STRING_KEY"] ?? ""];
|
||||||
|
options.UseSqlServer(connectionString, sqlOptions => sqlOptions.EnableRetryOnFailure());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.AddCookieSettings();
|
builder.Services.AddCookieSettings();
|
||||||
|
|
||||||
|
|||||||
6
src/Web/ViewModels/OrderDetailViewModel.cs
Normal file
6
src/Web/ViewModels/OrderDetailViewModel.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Microsoft.eShopWeb.Web.ViewModels;
|
||||||
|
|
||||||
|
public class OrderDetailViewModel : OrderViewModel
|
||||||
|
{
|
||||||
|
public List<OrderItemViewModel> OrderItems { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -11,5 +11,4 @@ public class OrderViewModel
|
|||||||
public decimal Total { get; set; }
|
public decimal Total { get; set; }
|
||||||
public string Status => DEFAULT_STATUS;
|
public string Status => DEFAULT_STATUS;
|
||||||
public Address? ShippingAddress { get; set; }
|
public Address? ShippingAddress { get; set; }
|
||||||
public List<OrderItemViewModel> OrderItems { get; set; } = new();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model OrderViewModel
|
@model OrderDetailViewModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "My Order History";
|
ViewData["Title"] = "My Order History";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
<PackageReference Include="Ardalis.ListStartupServices" />
|
<PackageReference Include="Ardalis.ListStartupServices" />
|
||||||
<PackageReference Include="Ardalis.Specification" />
|
<PackageReference Include="Ardalis.Specification" />
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" />
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" />
|
||||||
|
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" />
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
<PackageReference Include="MediatR" />
|
<PackageReference Include="MediatR" />
|
||||||
<PackageReference Include="BuildBundlerMinifier" Condition="'$(Configuration)'=='Release'" />
|
<PackageReference Include="BuildBundlerMinifier" Condition="'$(Configuration)'=='Release'" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<PackageReference Include="xunit" />
|
<PackageReference Include="xunit" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" />
|
<PackageReference Include="xunit.runner.visualstudio" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
|
||||||
<DotNetCliToolReference Include="dotnet-xunit" />
|
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" />
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||||
<PackageReference Include="Moq" />
|
<PackageReference Include="Moq" />
|
||||||
<PackageReference Include="xunit" />
|
<PackageReference Include="xunit" />
|
||||||
|
|||||||
Reference in New Issue
Block a user