Compare commits

..

6 Commits

Author SHA1 Message Date
Sumit Ghosh
38de4d5964 Updated packages to WebMVC 2021-10-20 16:06:21 +05:30
Sumit Ghosh
5448965cff Updated dockerfile 2021-10-20 16:05:45 +05:30
Sumit Ghosh
5e7de1617e Included file scope namespaces for all files 2021-10-20 16:04:59 +05:30
Sumit Ghosh
610707a5b7 Included globalusing WebMVC 2021-10-20 16:04:13 +05:30
Sumit Ghosh
ab1d9cc897
Updates basket-api to .NET 6 (#1742)
* Use global usings

* Use file-scoped namespaces

* Updates docker images to preview 7

* Created a new migration plan

* Included global usings for identity project

* Updated docker file to preview version to 7

* Updated dockerfiles

* Merged conent from Startup.cs to Program.cs

* Removed Starup.cs

* Removed unnecessary files

* Revert "Removed unnecessary files"

This reverts commit 536bddcd96b54673401cedbe802520dce12b3472.

* Revert "Removed Starup.cs"

This reverts commit 46175d7aa97475d88ec46bce39ed498c7037d924.

* Revert "Merged conent from Startup.cs to Program.cs"

This reverts commit 2766ea86dfef9220fe3f0c27a37a9a6c18153078.

* Removed extra spaces

* Updated basket-api project file

* Update src/Services/Basket/Basket.API/Grpc/BasketService.cs

Co-authored-by: David Pine <david.pine@microsoft.com>

* Apply suggestions from code review

Co-authored-by: David Pine <david.pine@microsoft.com>

* Moved the fully qualified namespace on top

* Updated relevant packages in basket.api project

* Updated relevant packages in identity.api project

Co-authored-by: David Pine <david.pine@microsoft.com>
2021-10-04 19:39:36 +05:30
Josh Coleman
8be1c9cd3e
Seeking feedback: Build and run on .Net 6 preview 6 (#1734)
* Updgrade build and hosting machines to .net6 latest

* Target .net 6

* ILogger is ambiguous?

* More ILogger ambiguity

* Use preview 6... seeing errors in preview 7...

* Of course the SDK version is different :)

* downgrade the last nonworking component

* Only restore the packages we need for the one off service stuck in .net 5

* Downgrade development docker files to use the preview 6 sdk
2021-08-09 19:57:22 +05:30
781 changed files with 65776 additions and 64522 deletions

View File

@ -1,132 +0,0 @@
###############################
# Core EditorConfig Options #
###############################
root = true
# All files
[*]
indent_style = space
# XML project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# XML config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
charset = utf-8-bom
###############################
# .NET Coding Conventions #
###############################
[*.{cs,vb}]
# Organize usings
dotnet_sort_system_directives_first = true
# this. preferences
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Naming Conventions #
###############################
# Style Definitions
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Use PascalCase for constant fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
dotnet_naming_symbols.constant_fields.required_modifiers = const
###############################
# C# Coding Conventions #
###############################
[*.cs]
# var preferences
csharp_style_var_for_built_in_types = true:silent
csharp_style_var_when_type_is_apparent = true:silent
csharp_style_var_elsewhere = true:silent
# Expression-bodied members
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
# Null-checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
# Expression-level preferences
csharp_prefer_braces = true:silent
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
###############################
# C# Formatting Rules #
###############################
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
###############################
# VB Coding Conventions #
###############################
[*.vb]
# Modifier preferences
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -22,6 +22,7 @@ on:
env: env:
SERVICE: basket-api SERVICE: basket-api
IMAGE: basket.api IMAGE: basket.api
DOTNET_VERSION: 5.0.x
jobs: jobs:
@ -29,24 +30,97 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with: with:
service: ${{ env.SERVICE }} dotnet-version: ${{ env.DOTNET_VERSION }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Build and run unit tests
run: |
cd src
dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
cd Services/Basket/Basket.API
dotnet build --no-restore
cd -
cd Services/Basket/Basket.UnitTests
dotnet build --no-restore
dotnet test --no-build -v=normal
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with: with:
service: ${{ env.SERVICE }} dotnet-version: ${{ env.DOTNET_VERSION }}
registry_host: ${{ secrets.REGISTRY_HOST }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} - name: Build and run unit tests
image_name: ${{ env.IMAGE }} run: |
registry_username: ${{ secrets.USERNAME }} cd src
registry_password: ${{ secrets.PASSWORD }} dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
cd Services/Basket/Basket.API
dotnet build --no-restore
cd -
cd Services/Basket/Basket.UnitTests
dotnet build --no-restore
dotnet test --no-build -v=normal
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with:
registry: ${{ secrets.REGISTRY_HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -22,9 +22,7 @@ on:
env: env:
SERVICE: catalog-api SERVICE: catalog-api
IMAGE: catalog.api IMAGE: catalog.api
DOTNET_VERSION: 7.0.x DOTNET_VERSION: 5.0.x
PROJECT_PATH: Services/Catalog/Catalog.API
TESTS_PATH: Services/Catalog/Catalog.UnitTests
jobs: jobs:
@ -32,27 +30,97 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-test
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with: with:
service: ${{ env.SERVICE }} dotnet-version: ${{ env.DOTNET_VERSION }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }}
dotnet_version: ${{ env.DOTNET_VERSION }} - name: Build and run unit tests
project_path: ${{ env.PROJECT_PATH }} run: |
tests_path: ${{ env.TESTS_PATH }} cd src
dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
cd Services/Catalog/Catalog.API
dotnet build --no-restore
cd -
cd Services/Catalog/Catalog.UnitTests
dotnet build --no-restore
dotnet test --no-build -v=normal
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with: with:
service: ${{ env.SERVICE }} dotnet-version: ${{ env.DOTNET_VERSION }}
registry_host: ${{ secrets.REGISTRY_HOST }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} - name: Build and run unit tests
image_name: ${{ env.IMAGE }} run: |
registry_username: ${{ secrets.USERNAME }} cd src
registry_password: ${{ secrets.PASSWORD }} dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
cd Services/Catalog/Catalog.API
dotnet build --no-restore
cd -
cd Services/Catalog/Catalog.UnitTests
dotnet build --no-restore
dotnet test --no-build -v=normal
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with:
registry: ${{ secrets.REGISTRY_HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -1,71 +0,0 @@
name: "Build and push image"
description: "Builds and pushes an image to a registry"
inputs:
service:
description: "Service to build"
required: true
registry_host:
description: "Image registry host e.g. myacr.azureacr.io"
required: true
registry_endpoint:
description: "Image registry repo e.g. myacr.azureacr.io/eshop"
required: true
image_name:
description: "Name of image"
required: true
registry_username:
description: "Registry username"
required: true
registry_password:
description: "Registry password"
required: true
runs:
using: "composite"
steps:
- name: Enable experimental features for the Docker daemon and CLI
shell: bash
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with:
registry: ${{ inputs.registry_host }}
username: ${{ inputs.registry_username }}
password: ${{ inputs.registry_password }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ inputs.service }}
shell: bash
run: sudo -E docker-compose build ${{ inputs.service }}
working-directory: ./src
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ inputs.registry_endpoint }}
- name: Compose push ${{ inputs.service }}
shell: bash
run: sudo -E docker-compose push ${{ inputs.service }}
working-directory: ./src
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ inputs.registry_endpoint }}
- name: Create multiarch manifest
shell: bash
run: |
docker --config ~/.docker manifest create ${{ inputs.registry_endpoint }}/${{ inputs.image_name }}:${{ env.BRANCH }} ${{ inputs.registry_endpoint }}/${{ inputs.image_name }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ inputs.registry_endpoint }}/${{ inputs.image_name }}:${{ env.BRANCH }}

View File

@ -1,47 +0,0 @@
name: "Build for PRe"
description: "Builds a docker image without pushing"
inputs:
service:
description: "Service to build"
required: true
registry_endpoint:
description: "Image registry repo e.g. myacr.azureacr.io/eshop"
required: true
dotnet_version:
description: "Version of dotnet to use for testing"
required: true
project_path:
description: "Path to project to test e.g. Services/Catalog/Catalog.API"
required: true
tests_path:
description: "Path to test project e.g. Services/Catalog/Catalog.UnitTests"
required: true
runs:
using: "composite"
steps:
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ inputs.dotnet_version }}
- name: Build and run unit tests
shell: bash
run: |
cd src
dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
cd ${{ inputs.project_path }}
dotnet build --no-restore
cd -
cd ${{ inputs.tests_path }}
dotnet build --no-restore
dotnet test --no-build -v=normal
- name: Compose build ${{ inputs.service }}
shell: bash
run: sudo -E docker-compose build ${{ inputs.service }}
working-directory: ./src
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ inputs.registry_endpoint }}

View File

@ -1,21 +0,0 @@
name: "Build for PRe"
description: "Builds a docker image without pushing"
inputs:
service:
description: "Service to build"
required: true
registry_endpoint:
description: "Image registry repo e.g. myacr.azureacr.io/eshop"
required: true
runs:
using: "composite"
steps:
- name: Compose build ${{ inputs.service }}
shell: bash
run: sudo -E docker-compose build ${{ inputs.service }}
working-directory: ./src
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ inputs.registry_endpoint }}

View File

@ -1,54 +0,0 @@
name: "Deploy Helm to AKS"
description: "Deploys a helm chart to AKS"
inputs:
azure_credentials:
description: "Credentials to connect to AKS"
required: true
cluster_name:
description: "Name of AKS cluster"
required: true
resource_group:
description: "Resource group of AKS cluster"
required: true
registry_host:
description: "Image registry host e.g. myacr.azureacr.io"
required: true
chart:
description: "Chart name"
required: true
chart_root:
description: "Root folder of chart"
required: true
namespace:
description: "Namespace to deploy to"
required: true
runs:
using: "composite"
steps:
- uses: azure/login@v1
with:
creds: ${{ inputs.azure_credentials }}
- uses: azure/aks-set-context@v1
name: Set AKS context
with:
creds: '${{ inputs.azure_credentials }}'
cluster-name: ${{ inputs.cluster_name }}
resource-group: ${{ inputs.resource_group }}
- name: Set branch name as env variable
shell: bash
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
- name: Deploy Chart
shell: bash
run: |
./deploy-chart.sh -c ${{ inputs.chart }} --dns aks --aks-name ${{ inputs.clusteR_name }} --aks-rg ${{ inputs.resource_group }} -r ${{ inputs.registry_host }} -t $TAG --namespace ${{ inputs.namespace }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ inputs.chart_root }}

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: identity-api name: identity-api
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -29,24 +28,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: mobileshoppingagg name: mobileshoppingagg
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -27,24 +26,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -22,9 +22,7 @@ on:
env: env:
SERVICE: ordering-api SERVICE: ordering-api
IMAGE: ordering.api IMAGE: ordering.api
DOTNET_VERSION: 7.0.x DOTNET_VERSION: 5.0.x
PROJECT_PATH: Services/Ordering/Ordering.API
TESTS_PATH: Services/Ordering/Ordering.UnitTests
jobs: jobs:
@ -32,27 +30,97 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-test
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with: with:
service: ${{ env.SERVICE }} dotnet-version: ${{ env.DOTNET_VERSION }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }}
dotnet_version: ${{ env.DOTNET_VERSION }} - name: Build and run unit tests
project_path: ${{ env.PROJECT_PATH }} run: |
tests_path: ${{ env.TESTS_PATH }} cd src
dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
cd Services/Ordering/Ordering.API
dotnet build --no-restore
cd -
cd Services/Ordering/Ordering.UnitTests
dotnet build --no-restore
dotnet test --no-build -v=normal
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with: with:
service: ${{ env.SERVICE }} dotnet-version: ${{ env.DOTNET_VERSION }}
registry_host: ${{ secrets.REGISTRY_HOST }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} - name: Build and run unit tests
image_name: ${{ env.IMAGE }} run: |
registry_username: ${{ secrets.USERNAME }} cd src
registry_password: ${{ secrets.PASSWORD }} dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"
cd Services/Ordering/Ordering.API
dotnet build --no-restore
cd -
cd Services/Ordering/Ordering.UnitTests
dotnet build --no-restore
dotnet test --no-build -v=normal
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with:
registry: ${{ secrets.REGISTRY_HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: ordering-backgroundtasks name: ordering-backgroundtasks
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -29,24 +28,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: ordering-signalrhub name: ordering-signalrhub
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -29,24 +28,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: payment-api name: payment-api
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -29,24 +28,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: webhooks-api name: webhooks-api
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -29,24 +28,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -1,7 +1,6 @@
name: webhooks-client name: webhooks-client
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -29,24 +28,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -28,12 +28,23 @@ jobs:
with: with:
creds: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/aks-set-context@v1
name: Set AKS context
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster_name: ${{ secrets.CLUSTER_NAME }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} resource-group: ${{ secrets.RESOURCE_GROUP }}
registry_host: ${{ secrets.REGISTRY_HOST }}
chart: ${{ env.CHART }} - name: Set branch name as env variable
chart_root: ${{ env.CHART_ROOT }} run: |
namespace: ${{ env.NAMESPACE }} currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: webmvc name: webmvc
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -30,24 +29,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: webshoppingagg name: webshoppingagg
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -27,24 +26,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: webspa name: webspa
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -29,24 +28,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

View File

@ -24,12 +24,27 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: ./.github/workflows/composite/deploy-helm - uses: azure/login@v1
with: with:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster_name: ${{ secrets.CLUSTER_NAME }}
resource_group: ${{ secrets.RESOURCE_GROUP }} - uses: azure/aks-set-context@v1
registry_host: ${{ secrets.REGISTRY_HOST }} name: Set AKS context
chart: ${{ env.CHART }} with:
chart_root: ${{ env.CHART_ROOT }} creds: '${{ secrets.AZURE_CREDENTIALS }}'
namespace: ${{ env.NAMESPACE }} cluster-name: ${{ secrets.CLUSTER_NAME }}
resource-group: ${{ secrets.RESOURCE_GROUP }}
- name: Set branch name as env variable
run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Deploy Chart
run: |
./deploy-chart.sh -c ${{ env.CHART }} --dns aks --aks-name ${{ secrets.CLUSTER_NAME }} --aks-rg ${{ secrets.RESOURCE_GROUP }} -r ${{ secrets.REGISTRY_HOST }} -t $TAG --namespace ${{ env.NAMESPACE }} --acr-connected
env:
TAG: ${{ env.BRANCH }}
working-directory: ${{ env.CHART_ROOT }}

View File

@ -1,7 +1,6 @@
name: webstatus name: webstatus
on: on:
workflow_dispatch:
push: push:
branches: branches:
- dev - dev
@ -30,24 +29,65 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build
with: - name: Compose build ${{ env.SERVICE }}
service: ${{ env.SERVICE }} run: sudo -E docker-compose build ${{ env.SERVICE }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
BuildLinux: BuildLinux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }} if: ${{ github.event_name != 'pull_request' }}
steps: steps:
- name: Checkout code - name: 'Checkout Github Action'
uses: actions/checkout@v2 uses: actions/checkout@master
- uses: ./.github/workflows/composite/build-push
- name: Enable experimental features for the Docker daemon and CLI
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker
echo $'{\n "experimental": "enabled"\n}' | sudo tee ~/.docker/config.json
sudo service docker restart
docker version -f '{{.Client.Experimental}}'
docker version -f '{{.Server.Experimental}}'
- name: Login to Container Registry
uses: docker/login-action@v1
with: with:
service: ${{ env.SERVICE }} registry: ${{ secrets.REGISTRY_HOST }}
registry_host: ${{ secrets.REGISTRY_HOST }} username: ${{ secrets.USERNAME }}
registry_endpoint: ${{ secrets.REGISTRY_ENDPOINT }} password: ${{ secrets.PASSWORD }}
image_name: ${{ env.IMAGE }}
registry_username: ${{ secrets.USERNAME }} - name: Set branch name as env variable
registry_password: ${{ secrets.PASSWORD }} run: |
currentbranch=$(echo ${GITHUB_REF##*/})
echo "running on $currentbranch"
echo "BRANCH=$currentbranch" >> $GITHUB_ENV
shell: bash
- name: Compose build ${{ env.SERVICE }}
run: sudo -E docker-compose build ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Compose push ${{ env.SERVICE }}
run: sudo -E docker-compose push ${{ env.SERVICE }}
working-directory: ./src
shell: bash
env:
TAG: ${{ env.BRANCH }}
REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }}
- name: Create multiarch manifest
run: |
docker --config ~/.docker manifest create ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }} ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:linux-${{ env.BRANCH }}
docker --config ~/.docker manifest push ${{ secrets.REGISTRY_ENDPOINT }}/${{ env.IMAGE }}:${{ env.BRANCH }}
shell: bash

2
.gitignore vendored
View File

@ -281,5 +281,3 @@ pub/
src/**/app.yaml src/**/app.yaml
src/**/inf.yaml src/**/inf.yaml
.angular/
/src/Services/Identity/Identity.API/keys/*.json

View File

@ -47,7 +47,7 @@ All contributions must be submitted as a [Pull Request (PR)](https://help.github
The main branches are **`dev`** and **`master`**: The main branches are **`dev`** and **`master`**:
- **`dev`**: Contains the latest code **and it is the branch actively developed**. - **`dev`**: Contains the latest code **and it is the branch actively developed**.
**All PRs must be against `dev` branch to be considered**. This branch is developed using `.NET 7` **All PRs must be against `dev` branch to be considered**. This branch is developed using `.NET 5`
- **`main`**: Synced from time to time from **`dev`**. It contains "stable" code.This branch contains changes specific to `.NET Core 3.1` (**Keep in mind "stable" does not mean PRODUCTION-READY!**) - **`main`**: Synced from time to time from **`dev`**. It contains "stable" code.This branch contains changes specific to `.NET Core 3.1` (**Keep in mind "stable" does not mean PRODUCTION-READY!**)

View File

@ -56,25 +56,25 @@ The basic scenario can be run locally using docker-compose, and also deployed to
### Advanced scenario ### Advanced scenario
The Advanced scenario can be run only in a Kubernetes cluster. Currently, this scenario is the same as a basic scenario with the following differences: The Advanced scenario can be run only in a Kubernetes cluster. Currently this scenario is the same as a basic scenario with the following differences:
- [Deploy to AKS with a Service Mesh for resiliency](https://github.com/dotnet-architecture/eShopOnContainers/wiki/Deploy-to-Azure-Kubernetes-Service-(AKS)) - [Deploy to AKS with a Service Mesh for resiliency](https://github.com/dotnet-architecture/eShopOnContainers/wiki/Deploy-to-Azure-Kubernetes-Service-(AKS))
In the future, more features will be implemented in the advanced scenario. In the future more features will be implemented in the advanced scenario.
## IMPORTANT NOTES! ## IMPORTANT NOTES!
**You can use either the latest version of Visual Studio or simply Docker CLI and .NET CLI for Windows, Mac, and Linux**. **You can use either the latest version of Visual Studio or simply Docker CLI and .NET CLI for Windows, Mac and Linux**.
**Note for Pull Requests (PRs)**: We accept pull requests from the community. When doing it, please do it onto the **DEV branch** which is the consolidated work-in-progress branch. Do not request it onto **main** branch. **Note for Pull Requests (PRs)**: We accept pull request from the community. When doing it, please do it onto the **DEV branch** which is the consolidated work-in-progress branch. Do not request it onto **master** branch.
**NEWS / ANNOUNCEMENTS** **NEWS / ANNOUNCEMENTS**
Do you want to be up-to-date on .NET Architecture guidance and reference apps like eShopOnContainers? --> Subscribe by "WATCHING" this new GitHub repo: https://github.com/dotnet-architecture/News Do you want to be up-to-date on .NET Architecture guidance and reference apps like eShopOnContainers? --> Subscribe by "WATCHING" this new GitHub repo: https://github.com/dotnet-architecture/News
## Updated for .NET 7 ## Updated for .NET 5
eShopOnContainers is updated to .NET 7 "wave" of technologies. Not just compilation but also new recommended code in EF Core, ASP.NET Core, and other new related versions with several significant changes. eShopOnContainers is updated to .NET 5 "wave" of technologies. Not just compilation but also new recommended code in EF Core, ASP.NET Core, and other new related versions with several significant changes.
**See more details in the [Release notes](https://github.com/dotnet-architecture/eShopOnContainers/wiki/Release-notes) wiki page**. **See more details in the [Release notes](https://github.com/dotnet-architecture/eShopOnContainers/wiki/Release-notes) wiki page**.
@ -86,15 +86,15 @@ eShopOnContainers is updated to .NET 7 "wave" of technologies. Not just compilat
### Architecture overview ### Architecture overview
This reference application is cross-platform at the server and client-side, thanks to .NET 7 services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS, or Windows/UWP plus any browser for the client web apps. This reference application is cross-platform at the server and client side, thanks to .NET 5 services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS or Windows/UWP plus any browser for the client web apps.
The architecture proposes a microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using HTTP as the communication protocol between the client apps and the microservices and supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus (a light message broker, to choose between RabbitMQ or Azure Service Bus, underneath) plus other features defined at the [roadmap](https://github.com/dotnet-architecture/eShopOnContainers/wiki/Roadmap). The architecture proposes a microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the communication protocol between the client apps and the microservices and supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus (a light message broker, to choose between RabbitMQ or Azure Service Bus, underneath) plus other features defined at the [roadmap](https://github.com/dotnet-architecture/eShopOnContainers/wiki/Roadmap).
![](img/eshop_logo.png) ![](img/eshop_logo.png)
![](img/eShopOnContainers-architecture.png) ![](img/eShopOnContainers-architecture.png)
## Related documentation and guidance ## Related documentation and guidance
You can find the related reference **Guide/eBook** focusing on **architecting and developing containerized and microservice-based .NET Applications** (download link available below) which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic apps that can also live as Docker containers. You can find the related reference **Guide/eBook** focusing on **architecting and developing containerized and microservice based .NET Applications** (download link available below) which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic apps that can also live as Docker containers.
There are also additional eBooks focusing on Containers/Docker lifecycle (DevOps, CI/CD, etc.) with Microsoft Tools, already published plus an additional eBook focusing on Enterprise Apps Patterns with Xamarin.Forms. There are also additional eBooks focusing on Containers/Docker lifecycle (DevOps, CI/CD, etc.) with Microsoft Tools, already published plus an additional eBook focusing on Enterprise Apps Patterns with Xamarin.Forms.
You can download them and start reviewing these Guides/eBooks here: You can download them and start reviewing these Guides/eBooks here:
@ -107,7 +107,7 @@ You can download them and start reviewing these Guides/eBooks here:
For more free e-Books check out [.NET Architecture center](https://dot.net/architecture). If you have an e-book feedback, let us know by creating a new issue here: <https://github.com/dotnet-architecture/ebooks/issues> For more free e-Books check out [.NET Architecture center](https://dot.net/architecture). If you have an e-book feedback, let us know by creating a new issue here: <https://github.com/dotnet-architecture/ebooks/issues>
## Are you new to **microservices** and **cloud-native development**? ## Are you new to **microservices** and **cloud-native development**?
Take a look at the free course [Create and deploy a cloud-native ASP.NET Core microservice](https://docs.microsoft.com/en-us/learn/modules/microservices-aspnet-core/) on MS Learn. This module explains microservices concepts, cloud-native technologies, and reduces the friction in getting started with `eShopOnContainers`. Take a look at the free course [Create and deploy a cloud-native ASP.NET Core microservice](https://docs.microsoft.com/en-us/learn/modules/microservices-aspnet-core/) on MS Learn. This module explains microservices concepts, cloud-native technologies, and reduce the friction in getting started with `eShopOnContainers`.
## Read further ## Read further

View File

@ -2,12 +2,8 @@
Following are the most important branches: Following are the most important branches:
- `dev`: Contains the latest code **and it is the branch actively developed**. Note that **all PRs must be against the `dev` branch to be considered**. This branch is developed using `.NET 7` - `dev`: Contains the latest code **and it is the branch actively developed**. Note that **all PRs must be against the `dev` branch to be considered**. This branch is developed using `.NET 5`
- `release/net-6`: Contains the code changes specific to the `.NET 6` - `main`: Synced time to time from `dev`.It contains "stable" code, although not the latest one. Right now, this branch contains changes specific to `.NET Core 3.1`
- `release/net-5`: Contains the code changes specific to the `.NET 5`
- `release/net-3.1.1`: Contains the code changes specific to the `.NET 3.1`
> [!DISCLAIMER]: The `main` branch contains the old code base and will get obsolete in the future. So it's recommended to refer to different [tags](https://github.com/dotnet-architecture/eShopOnContainers/tags) to avoid any confusion.
Any other branch is considered temporary and could be deleted at any time. Do not submit any PR against them! Any other branch is considered temporary and could be deleted at any time. Do not submit any PR against them!

View File

@ -31,3 +31,5 @@ $services |% {
Write-Host "Setting ACR build $bname ($bimg)" Write-Host "Setting ACR build $bname ($bimg)"
az acr build-task create --registry $acrName --name $bname --image ${bimg}:$gitBranch --context $gitContext --branch $gitBranch --git-access-token $patToken --file $bfile az acr build-task create --registry $acrName --name $bname --image ${bimg}:$gitBranch --context $gitContext --branch $gitBranch --git-access-token $patToken --file $bfile
} }
# Basket.API

View File

@ -16,9 +16,6 @@
"OrderingSubscriptionName": "Ordering", "OrderingSubscriptionName": "Ordering",
"GracePeriodSubscriptionName": "GracePeriod", "GracePeriodSubscriptionName": "GracePeriod",
"PaymentSubscriptionName": "Payment", "PaymentSubscriptionName": "Payment",
"BackgroundTaskSubscriptionName": "backgroundtasks",
"OrderingSignalrHubSubscriptionName": "Ordering.signalrhub",
"WebhooksSubscriptionName": "Webhooks",
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"sbVersion": "2015-08-01", "sbVersion": "2015-08-01",
"defaultSASKeyName": "Root", "defaultSASKeyName": "Root",
@ -175,66 +172,6 @@
"autoDeleteOnIdle": "10675199.02:48:05.4775807", "autoDeleteOnIdle": "10675199.02:48:05.4775807",
"entityAvailabilityStatus": "Available" "entityAvailabilityStatus": "Available"
} }
},
{
"apiVersion": "[variables('sbVersion')]",
"name": "[variables('BackgroundTaskSubscriptionName')]",
"type": "Subscriptions",
"dependsOn": [
"[variables('serviceBusTopicName')]"
],
"properties": {
"lockDuration": "00:00:30",
"requiresSession": false,
"defaultMessageTimeToLive": "14.00:00:00",
"deadLetteringOnMessageExpiration": true,
"deadLetteringOnFilterEvaluationExceptions": true,
"maxDeliveryCount": 10,
"enableBatchedOperations": false,
"status": "Active",
"autoDeleteOnIdle": "10675199.02:48:05.4775807",
"entityAvailabilityStatus": "Available"
}
},
{
"apiVersion": "[variables('sbVersion')]",
"name": "[variables('OrderingSignalrHubSubscriptionName')]",
"type": "Subscriptions",
"dependsOn": [
"[variables('serviceBusTopicName')]"
],
"properties": {
"lockDuration": "00:00:30",
"requiresSession": false,
"defaultMessageTimeToLive": "14.00:00:00",
"deadLetteringOnMessageExpiration": true,
"deadLetteringOnFilterEvaluationExceptions": true,
"maxDeliveryCount": 10,
"enableBatchedOperations": false,
"status": "Active",
"autoDeleteOnIdle": "10675199.02:48:05.4775807",
"entityAvailabilityStatus": "Available"
}
},
{
"apiVersion": "[variables('sbVersion')]",
"name": "[variables('WebhooksSubscriptionName')]",
"type": "Subscriptions",
"dependsOn": [
"[variables('serviceBusTopicName')]"
],
"properties": {
"lockDuration": "00:00:30",
"requiresSession": false,
"defaultMessageTimeToLive": "14.00:00:00",
"deadLetteringOnMessageExpiration": true,
"deadLetteringOnFilterEvaluationExceptions": true,
"maxDeliveryCount": 10,
"enableBatchedOperations": false,
"status": "Active",
"autoDeleteOnIdle": "10675199.02:48:05.4775807",
"entityAvailabilityStatus": "Available"
}
} }
] ]
} }

View File

@ -30,7 +30,7 @@ Write-Host "Creating AKS $resourceGroupName/$serviceName" -ForegroundColor Yello
az aks create --resource-group=$resourceGroupName --name=$serviceName --dns-name-prefix=$dnsNamePrefix --generate-ssh-keys --node-count=$nodeCount --node-vm-size=$nodeVMSize --vm-set-type $vmSetType az aks create --resource-group=$resourceGroupName --name=$serviceName --dns-name-prefix=$dnsNamePrefix --generate-ssh-keys --node-count=$nodeCount --node-vm-size=$nodeVMSize --vm-set-type $vmSetType
if ($enableHttpApplicationAddon) { if ($enableHttpApplicationAddon) {
Write-Host "Enabling Http Application Routing in AKS $serviceName" -ForegroundColor Yellow Write-Host "Enabling Http Applciation Routing in AKS $serviceName" -ForegroundColor Yellow
az aks enable-addons --resource-group $resourceGroupName --name $serviceName --addons http_application_routing az aks enable-addons --resource-group $resourceGroupName --name $serviceName --addons http_application_routing
} }

View File

@ -9,20 +9,19 @@ static_resources:
- address: - address:
socket_address: socket_address:
address: 0.0.0.0 address: 0.0.0.0
port_value: 8080 port_value: 80
filter_chains: filter_chains:
- filters: - filters:
- name: envoy.filters.network.http_connection_manager - name: envoy.http_connection_manager
typed_config: config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: auto
stat_prefix: ingress_http stat_prefix: ingress_http
codec_type: AUTO
route_config: route_config:
name: eshop_backend_route name: eshop_backend_route
virtual_hosts: virtual_hosts:
- name: eshop_backend - name: eshop_backend
domains: domains:
- ["*"] - "*"
routes: routes:
- name: "c-short" - name: "c-short"
match: match:
@ -78,70 +77,63 @@ static_resources:
prefix_rewrite: "/" prefix_rewrite: "/"
cluster: shoppingagg cluster: shoppingagg
http_filters: http_filters:
- name: envoy.filters.http.router - name: envoy.router
access_log:
- name: envoy.file_access_log
filter:
not_health_check_filter: {}
config:
json_format:
time: "%START_TIME%"
protocol: "%PROTOCOL%"
duration: "%DURATION%"
request_method: "%REQ(:METHOD)%"
request_host: "%REQ(HOST)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
response_flags: "%RESPONSE_FLAGS%"
route_name: "%ROUTE_NAME%"
upstream_host: "%UPSTREAM_HOST%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%"
path: "/tmp/access.log"
clusters: clusters:
- name: shoppingagg - name: shoppingagg
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: shoppingagg - socket_address:
endpoints: address: webshoppingagg
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: webshoppingagg
port_value: 80
- name: catalog - name: catalog
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: catalog - socket_address:
endpoints: address: catalog-api
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: catalog-api
port_value: 80
- name: basket - name: basket
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: basket - socket_address:
endpoints: address: basket-api
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: basket-api
port_value: 80
- name: ordering - name: ordering
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: ordering - socket_address:
endpoints: address: ordering-api
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: ordering-api
port_value: 80
- name: signalr-hub - name: signalr-hub
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: signalr-hub - socket_address:
endpoints: address: ordering-signalrhub
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: ordering-signalrhub
port_value: 80

View File

@ -27,10 +27,6 @@ spec:
linkerd.io/inject: enabled linkerd.io/inject: enabled
{{- end }} {{- end }}
spec: spec:
securityContext:
runAsUser: 2000
runAsGroup: 3000
fsGroup: 2000
{{ if .Values.inf.registry -}} {{ if .Values.inf.registry -}}
imagePullSecrets: imagePullSecrets:
- name: {{ .Values.inf.registry.secretName }} - name: {{ .Values.inf.registry.secretName }}
@ -92,7 +88,7 @@ spec:
{{- end }} {{- end }}
ports: ports:
- name: http - name: http
containerPort: 8080 containerPort: 80
protocol: TCP protocol: TCP
- name: admin - name: admin
containerPort: 8001 containerPort: 8001

View File

@ -39,7 +39,7 @@ spec:
- host: {{ . }} - host: {{ . }}
http: http:
paths: paths:
- path: {{ $ingressPath }}(/|$)(.*) - path: {{ $ingressPath }}
pathType: Prefix pathType: Prefix
backend: backend:
service: service:

View File

@ -4,7 +4,7 @@ pathBase: /mobileshoppingapigw
image: image:
repository: envoyproxy/envoy repository: envoyproxy/envoy
tag: v1.21.0 tag: v1.11.1
service: service:
type: ClusterIP type: ClusterIP
@ -14,9 +14,8 @@ service:
ingress: ingress:
enabled: true enabled: true
annotations: annotations:
kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/rewrite-target: /$2 ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/use-regex: "true"
tls: [] tls: []
resources: {} resources: {}

View File

@ -9,20 +9,19 @@ static_resources:
- address: - address:
socket_address: socket_address:
address: 0.0.0.0 address: 0.0.0.0
port_value: 8080 port_value: 80
filter_chains: filter_chains:
- filters: - filters:
- name: envoy.filters.network.http_connection_manager - name: envoy.http_connection_manager
typed_config: config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: auto
stat_prefix: ingress_http stat_prefix: ingress_http
codec_type: AUTO
route_config: route_config:
name: eshop_backend_route name: eshop_backend_route
virtual_hosts: virtual_hosts:
- name: eshop_backend - name: eshop_backend
domains: domains:
- ["*"] - "*"
routes: routes:
- name: "c-short" - name: "c-short"
match: match:
@ -81,70 +80,63 @@ static_resources:
prefix_rewrite: "/" prefix_rewrite: "/"
cluster: shoppingagg cluster: shoppingagg
http_filters: http_filters:
- name: envoy.filters.http.router - name: envoy.router
access_log:
- name: envoy.file_access_log
filter:
not_health_check_filter: {}
config:
json_format:
time: "%START_TIME%"
protocol: "%PROTOCOL%"
duration: "%DURATION%"
request_method: "%REQ(:METHOD)%"
request_host: "%REQ(HOST)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
response_flags: "%RESPONSE_FLAGS%"
route_name: "%ROUTE_NAME%"
upstream_host: "%UPSTREAM_HOST%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%"
path: "/tmp/access.log"
clusters: clusters:
- name: shoppingagg - name: shoppingagg
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: shoppingagg - socket_address:
endpoints: address: webshoppingagg
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: webshoppingagg
port_value: 80
- name: catalog - name: catalog
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: catalog - socket_address:
endpoints: address: catalog-api
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: catalog-api
port_value: 80
- name: basket - name: basket
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: basket - socket_address:
endpoints: address: basket-api
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: basket-api
port_value: 80
- name: ordering - name: ordering
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: ordering - socket_address:
endpoints: address: ordering-api
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: ordering-api
port_value: 80
- name: signalr-hub - name: signalr-hub
connect_timeout: 0.25s connect_timeout: 0.25s
type: strict_dns type: strict_dns
lb_policy: round_robin lb_policy: round_robin
load_assignment: hosts:
cluster_name: signalr-hub - socket_address:
endpoints: address: ordering-signalrhub
- lb_endpoints: port_value: 80
- endpoint:
address:
socket_address:
address: ordering-signalrhub
port_value: 80

View File

@ -26,10 +26,6 @@ spec:
linkerd.io/inject: enabled linkerd.io/inject: enabled
{{- end }} {{- end }}
spec: spec:
securityContext:
runAsUser: 2000
runAsGroup: 3000
fsGroup: 2000
{{ if .Values.inf.registry -}} {{ if .Values.inf.registry -}}
imagePullSecrets: imagePullSecrets:
- name: {{ .Values.inf.registry.secretName }} - name: {{ .Values.inf.registry.secretName }}
@ -91,7 +87,7 @@ spec:
{{- end }} {{- end }}
ports: ports:
- name: http - name: http
containerPort: 8080 containerPort: 80
protocol: TCP protocol: TCP
- name: admin - name: admin
containerPort: 8001 containerPort: 8001

View File

@ -38,7 +38,7 @@ spec:
- host: {{ . }} - host: {{ . }}
http: http:
paths: paths:
- path: {{ $ingressPath }}(/|$)(.*) - path: {{ $ingressPath }}
pathType: Prefix pathType: Prefix
backend: backend:
service: service:

View File

@ -4,7 +4,7 @@ pathBase: /webshoppingapigw
image: image:
repository: envoyproxy/envoy repository: envoyproxy/envoy
tag: v1.21.0 tag: v1.11.1
service: service:
type: ClusterIP type: ClusterIP
@ -14,9 +14,8 @@ service:
ingress: ingress:
enabled: true enabled: true
annotations: annotations:
kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/rewrite-target: /$2 ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/use-regex: "true"
tls: [] tls: []
resources: {} resources: {}

View File

@ -13,7 +13,7 @@ metadata:
release: {{ .Release.Name }} release: {{ .Release.Name }}
heritage: {{ .Release.Service }} heritage: {{ .Release.Service }}
data: data:
catalog__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.catalog.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};TrustServerCertificate={{ .Values.inf.sql.common.TrustServerCertificate }}; catalog__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.catalog.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
catalog__PicBaseUrl: {{ $protocol }}://{{ $webshoppingapigw }}/c/api/v1/catalog/items/[0]/pic/ catalog__PicBaseUrl: {{ $protocol }}://{{ $webshoppingapigw }}/c/api/v1/catalog/items/[0]/pic/
catalog__AzureStorageEnabled: "{{ .Values.inf.misc.useAzureStorage }}" catalog__AzureStorageEnabled: "{{ .Values.inf.misc.useAzureStorage }}"
all__EventBusConnection: {{ .Values.inf.eventbus.constr }} all__EventBusConnection: {{ .Values.inf.eventbus.constr }}

View File

@ -43,10 +43,6 @@ Parameters:
The Docker username used to logon to the custom registry, supplied using the -r parameter. The Docker username used to logon to the custom registry, supplied using the -r parameter.
--use-local-k8s --use-local-k8s
Deploy to a locally installed Kubernetes (default: false). Deploy to a locally installed Kubernetes (default: false).
--use-mesh
Use Linkerd as service mesh
--image-pull-policy <policy>
Image Pull Policy: Always, IfNotPresent, Never (default: Always)
It is assumed that the Kubernetes cluster has been granted access to the container registry. It is assumed that the Kubernetes cluster has been granted access to the container registry.
If using AKS and ACR see link for more info: If using AKS and ACR see link for more info:
@ -76,9 +72,6 @@ push_images=''
skip_infrastructure='' skip_infrastructure=''
use_local_k8s='' use_local_k8s=''
namespace='eshop' namespace='eshop'
use_mesh='false'
ingressMeshAnnotationsFile='ingress_values_linkerd.yaml'
imagePullPolicy='Always'
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
@ -114,21 +107,12 @@ while [[ $# -gt 0 ]]; do
use_local_k8s='yes'; shift ;; use_local_k8s='yes'; shift ;;
--namespace ) --namespace )
namespace="$2"; shift 2;; namespace="$2"; shift 2;;
--use-mesh )
use_mesh='true'; shift ;;
--image-pull-policy )
imagePullPolicy="$2"; shift 2;;
*) *)
echo "Unknown option $1" echo "Unknown option $1"
usage; exit 2 ;; usage; exit 2 ;;
esac esac
done done
if [[ $imagePullPolicy != "Always" && $imagePullPolicy != "Never" && $imagePullPolicy != "IfNotPresent" ]]; then
echo "--image-pull-policy needs to be a valid value: Always, IfNotPresent, Never"
usage; exit 2;
fi
if [[ $build_solution ]]; then if [[ $build_solution ]]; then
echo "#################### Building $app_name solution ####################" echo "#################### Building $app_name solution ####################"
dotnet publish -o obj/Docker/publish ../../eShopOnContainers-ServicesAndWebApps.sln dotnet publish -o obj/Docker/publish ../../eShopOnContainers-ServicesAndWebApps.sln
@ -217,7 +201,7 @@ if [[ $clean ]]; then
if [[ -z $(helm ls -q --namespace $namespace) ]]; then if [[ -z $(helm ls -q --namespace $namespace) ]]; then
echo "No previous releases found" echo "No previous releases found"
else else
helm --namespace $namespace uninstall $(helm ls -q --namespace $namespace) helm uninstall $(helm ls -q --namespace $namespace)
echo "Previous releases deleted" echo "Previous releases deleted"
waitsecs=10; while [ $waitsecs -gt 0 ]; do echo -ne "$waitsecs\033[0K\r"; sleep 1; : $((waitsecs--)); done waitsecs=10; while [ $waitsecs -gt 0 ]; do echo -ne "$waitsecs\033[0K\r"; sleep 1; : $((waitsecs--)); done
fi fi
@ -225,14 +209,13 @@ fi
echo "#################### Begin $app_name installation using Helm ####################" echo "#################### Begin $app_name installation using Helm ####################"
infras=(sql-data nosql-data rabbitmq keystore-data basket-data) infras=(sql-data nosql-data rabbitmq keystore-data basket-data)
charts=(eshop-common basket-api catalog-api identity-api mobileshoppingagg ordering-api ordering-backgroundtasks ordering-signalrhub payment-api webmvc webshoppingagg webspa webstatus webhooks-api webhooks-web) charts=(eshop-common apigwms apigwws basket-api catalog-api identity-api mobileshoppingagg ordering-api ordering-backgroundtasks ordering-signalrhub payment-api webmvc webshoppingagg webspa webstatus webhooks-api webhooks-web)
gateways=(apigwms apigwws)
if [[ !$skip_infrastructure ]]; then if [[ !$skip_infrastructure ]]; then
for infra in "${infras[@]}" for infra in "${infras[@]}"
do do
echo "Installing infrastructure: $infra" echo "Installing infrastructure: $infra"
helm install "$app_name-$infra" --namespace $namespace --set "ingress.hosts={$dns}" --values app.yaml --values inf.yaml --values $ingress_values_file --values $ingressMeshAnnotationsFile --set app.name=$app_name --set inf.k8s.dns=$dns $infra --set inf.mesh.enabled=$use_mesh helm install "$app_name-$infra" --namespace $namespace --set "ingress.hosts={$dns}" --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns $infra
done done
fi fi
@ -240,16 +223,10 @@ for chart in "${charts[@]}"
do do
echo "Installing: $chart" echo "Installing: $chart"
if [[ $use_custom_registry ]]; then if [[ $use_custom_registry ]]; then
helm install "$app_name-$chart" --namespace $namespace --set "ingress.hosts={$dns}" --set inf.registry.server=$container_registry --set inf.registry.login=$docker_username --set inf.registry.pwd=$docker_password --set inf.registry.secretName=eshop-docker-scret --values app.yaml --values inf.yaml --values $ingress_values_file --values $ingressMeshAnnotationsFile --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=$imagePullPolicy $chart --set inf.mesh.enabled=$use_mesh helm install "$app_name-$chart" --namespace $namespace --set "ingress.hosts={$dns}" --set inf.registry.server=$container_registry --set inf.registry.login=$docker_username --set inf.registry.pwd=$docker_password --set inf.registry.secretName=eshop-docker-scret --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always $chart
elif [[ $chart != "eshop-common" ]]; then # eshop-common is ignored when no secret must be deployed elif [[ $chart != "eshop-common" ]]; then # eshop-common is ignored when no secret must be deployed
helm install "$app_name-$chart" --namespace $namespace --set "ingress.hosts={$dns}" --values app.yaml --values inf.yaml --values $ingress_values_file --values $ingressMeshAnnotationsFile --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=$imagePullPolicy $chart --set inf.mesh.enabled=$use_mesh helm install "$app_name-$chart" --namespace $namespace --set "ingress.hosts={$dns}" --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always $chart
fi fi
done done
for gw in "${gateways[@]}"
do
echo "Installing gateway: $gw"
helm install "$app_name-$gw" --namespace $namespace --set "ingress.hosts={$dns}" --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.pullPolicy=$imagePullPolicy $gw
done
echo "FINISHED: Helm charts installed." echo "FINISHED: Helm charts installed."

View File

@ -20,7 +20,7 @@ metadata:
release: {{ .Release.Name }} release: {{ .Release.Name }}
heritage: {{ .Release.Service }} heritage: {{ .Release.Service }}
data: data:
identity__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.identity.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};TrustServerCertificate={{ .Values.inf.sql.common.TrustServerCertificate }}; identity__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.identity.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
identity__keystore: {{ .Values.inf.redis.keystore.constr }} identity__keystore: {{ .Values.inf.redis.keystore.constr }}
all__InstrumentationKey: "{{ .Values.inf.appinsights.key }}" all__InstrumentationKey: "{{ .Values.inf.appinsights.key }}"
mvc_e: http://{{ $mvc_url }} mvc_e: http://{{ $mvc_url }}

View File

@ -13,7 +13,6 @@ inf:
user: sa # SQL user user: sa # SQL user
pwd: Pass@word # SQL pwd pwd: Pass@word # SQL pwd
pid: Developer pid: Developer
TrustServerCertificate: true
catalog: # inf.sql.catalog: settings for the catalog-api sql (user, pwd, db) catalog: # inf.sql.catalog: settings for the catalog-api sql (user, pwd, db)
db: CatalogDb # Catalog API SQL db name db: CatalogDb # Catalog API SQL db name
ordering: # inf.sql.ordering: settings for the ordering-api sql (user, pwd, db) ordering: # inf.sql.ordering: settings for the ordering-api sql (user, pwd, db)

View File

@ -11,7 +11,7 @@ metadata:
release: {{ .Release.Name }} release: {{ .Release.Name }}
heritage: {{ .Release.Service }} heritage: {{ .Release.Service }}
data: data:
ordering__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.ordering.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};TrustServerCertificate={{ .Values.inf.sql.common.TrustServerCertificate }}; ordering__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.ordering.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
urls__IdentityUrl: http://{{ .Values.app.svc.identity }} urls__IdentityUrl: http://{{ .Values.app.svc.identity }}
all__EventBusConnection: {{ .Values.inf.eventbus.constr }} all__EventBusConnection: {{ .Values.inf.eventbus.constr }}
all__InstrumentationKey: "{{ .Values.inf.appinsights.key }}" all__InstrumentationKey: "{{ .Values.inf.appinsights.key }}"

View File

@ -12,7 +12,7 @@ metadata:
release: {{ .Release.Name }} release: {{ .Release.Name }}
heritage: {{ .Release.Service }} heritage: {{ .Release.Service }}
data: data:
ordering__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.ordering.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};TrustServerCertificate={{ .Values.inf.sql.common.TrustServerCertificate }}; ordering__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.ordering.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
ordering__EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}" ordering__EnableLoadTest: "{{ .Values.inf.misc.useLoadTest }}"
all__EventBusConnection: {{ .Values.inf.eventbus.constr }} all__EventBusConnection: {{ .Values.inf.eventbus.constr }}
all__InstrumentationKey: "{{ .Values.inf.appinsights.key }}" all__InstrumentationKey: "{{ .Values.inf.appinsights.key }}"

View File

@ -13,7 +13,7 @@ metadata:
release: {{ .Release.Name }} release: {{ .Release.Name }}
heritage: {{ .Release.Service }} heritage: {{ .Release.Service }}
data: data:
webhooks__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.webhooks.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};TrustServerCertificate={{ .Values.inf.sql.common.TrustServerCertificate }}; webhooks__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.webhooks.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
urls__IdentityUrl: http://{{ $identity }} urls__IdentityUrl: http://{{ $identity }}
urls__IdentityUrlExternal: {{ $protocol }}://{{ $identity }} urls__IdentityUrlExternal: {{ $protocol }}://{{ $identity }}
all__EventBusConnection: {{ .Values.inf.eventbus.constr }} all__EventBusConnection: {{ .Values.inf.eventbus.constr }}

View File

@ -4,7 +4,7 @@ metadata:
labels: labels:
app.kubernetes.io/name: ingress-nginx app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/part-of: ingress-nginx
name: ingress-nginx-controller name: nginx-configuration
namespace: ingress-nginx namespace: ingress-nginx
data: data:
proxy-buffer-size: "128k" proxy-buffer-size: "128k"

View File

@ -11,16 +11,15 @@ metadata:
namespace: default namespace: default
spec: spec:
rules: rules:
- host: localhost http:
http: paths:
paths: - path: /webmvc
- path: /webmvc pathType: Prefix
pathType: Prefix backend:
backend: service:
service: name: webmvc
name: webmvc port:
port: number: 80
number: 80
--- ---
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
@ -35,13 +34,12 @@ metadata:
namespace: default namespace: default
spec: spec:
rules: rules:
- host: localhost http:
http: paths:
paths: - path: /identity
- path: /identity pathType: Prefix
pathType: Prefix backend:
backend: service:
service: name: identity
name: identity port:
port: number: 80
number: 80

Binary file not shown.

View File

@ -6,15 +6,15 @@
# Use this values to run the app locally in Windows # Use this values to run the app locally in Windows
ESHOP_EXTERNAL_DNS_NAME_OR_IP=host.docker.internal ESHOP_EXTERNAL_DNS_NAME_OR_IP=host.docker.internal
ESHOP_STORAGE_CATALOG_URL=http://host.docker.internal:5121/c/api/v1/catalog/items/[0]/pic/ ESHOP_STORAGE_CATALOG_URL=http://host.docker.internal:5202/c/api/v1/catalog/items/[0]/pic/
# Use this values to run the app locally in Mac # Use this values to run the app locally in Mac
# ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.mac.localhost # ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.mac.localhost
# ESHOP_STORAGE_CATALOG_URL=http://docker.for.mac.localhost:5121/c/api/v1/catalog/items/[0]/pic/ # ESHOP_STORAGE_CATALOG_URL=http://docker.for.mac.localhost:5202/c/api/v1/catalog/items/[0]/pic/
# Use this values to run the app locally in Linux # Use this values to run the app locally in Linux
# ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.linux.localhost # ESHOP_EXTERNAL_DNS_NAME_OR_IP=docker.for.linux.localhost
# ESHOP_STORAGE_CATALOG_URL=http://docker.for.linux.localhost:5121/c/api/v1/catalog/items/[0]/pic/ # ESHOP_STORAGE_CATALOG_URL=http://docker.for.linux.localhost:5202/c/api/v1/catalog/items/[0]/pic/
# Configure this values to the cloud storage locations # Configure this values to the cloud storage locations
# ESHOP_STORAGE_CATALOG_URL=<YourAzureStorage_Catalog_BLOB_URL> # ESHOP_STORAGE_CATALOG_URL=<YourAzureStorage_Catalog_BLOB_URL>

View File

@ -0,0 +1,139 @@
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 80
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
route_config:
name: eshop_backend_route
virtual_hosts:
- name: eshop_backend
domains:
- "*"
routes:
- name: "c-short"
match:
prefix: "/c/"
route:
auto_host_rewrite: true
prefix_rewrite: "/catalog-api/"
cluster: catalog
- name: "c-long"
match:
prefix: "/catalog-api/"
route:
auto_host_rewrite: true
cluster: catalog
- name: "o-short"
match:
prefix: "/o/"
route:
auto_host_rewrite: true
prefix_rewrite: "/ordering-api/"
cluster: ordering
- name: "o-long"
match:
prefix: "/ordering-api/"
route:
auto_host_rewrite: true
cluster: ordering
- name: "h-long"
match:
prefix: "/hub/notificationhub"
route:
auto_host_rewrite: true
cluster: signalr-hub
timeout: 300s
- name: "b-short"
match:
prefix: "/b/"
route:
auto_host_rewrite: true
prefix_rewrite: "/basket-api/"
cluster: basket
- name: "b-long"
match:
prefix: "/basket-api/"
route:
auto_host_rewrite: true
cluster: basket
- name: "agg"
match:
prefix: "/"
route:
auto_host_rewrite: true
prefix_rewrite: "/"
cluster: shoppingagg
http_filters:
- name: envoy.router
access_log:
- name: envoy.file_access_log
filter:
not_health_check_filter: {}
config:
json_format:
time: "%START_TIME%"
protocol: "%PROTOCOL%"
duration: "%DURATION%"
request_method: "%REQ(:METHOD)%"
request_host: "%REQ(HOST)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
response_flags: "%RESPONSE_FLAGS%"
route_name: "%ROUTE_NAME%"
upstream_host: "%UPSTREAM_HOST%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%"
path: "/tmp/access.log"
clusters:
- name: shoppingagg
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: mobileshoppingagg
port_value: 80
- name: catalog
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: catalog-api
port_value: 80
- name: basket
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: basket-api
port_value: 80
- name: ordering
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: ordering-api
port_value: 80
- name: signalr-hub
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: ordering-signalrhub
port_value: 80

View File

@ -0,0 +1,142 @@
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 80
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
codec_type: auto
stat_prefix: ingress_http
route_config:
name: eshop_backend_route
virtual_hosts:
- name: eshop_backend
domains:
- "*"
routes:
- name: "c-short"
match:
prefix: "/c/"
route:
auto_host_rewrite: true
prefix_rewrite: "/catalog-api/"
cluster: catalog
- name: "c-long"
match:
prefix: "/catalog-api/"
route:
auto_host_rewrite: true
cluster: catalog
- name: "o-short"
match:
prefix: "/o/"
route:
auto_host_rewrite: true
prefix_rewrite: "/ordering-api/"
cluster: ordering
- name: "o-long"
match:
prefix: "/ordering-api/"
route:
auto_host_rewrite: true
cluster: ordering
- name: "h-long"
match:
prefix: "/hub/notificationhub"
route:
auto_host_rewrite: true
cluster: signalr-hub
timeout: 300s
upgrade_configs:
upgrade_type: "websocket"
enabled: true
- name: "b-short"
match:
prefix: "/b/"
route:
auto_host_rewrite: true
prefix_rewrite: "/basket-api/"
cluster: basket
- name: "b-long"
match:
prefix: "/basket-api/"
route:
auto_host_rewrite: true
cluster: basket
- name: "agg"
match:
prefix: "/"
route:
auto_host_rewrite: true
prefix_rewrite: "/"
cluster: shoppingagg
http_filters:
- name: envoy.router
access_log:
- name: envoy.file_access_log
filter:
not_health_check_filter: {}
config:
json_format:
time: "%START_TIME%"
protocol: "%PROTOCOL%"
duration: "%DURATION%"
request_method: "%REQ(:METHOD)%"
request_host: "%REQ(HOST)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
response_flags: "%RESPONSE_FLAGS%"
route_name: "%ROUTE_NAME%"
upstream_host: "%UPSTREAM_HOST%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%"
path: "/tmp/access.log"
clusters:
- name: shoppingagg
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: webshoppingagg
port_value: 80
- name: catalog
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: catalog-api
port_value: 80
- name: basket
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: basket-api
port_value: 80
- name: ordering
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: ordering-api
port_value: 80
- name: signalr-hub
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
hosts:
- socket_address:
address: ordering-signalrhub
port_value: 80

View File

@ -1,35 +1,38 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; using System.Collections.Generic;
public class UrlsConfig namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config
{ {
public class CatalogOperations public class UrlsConfig
{ {
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; public class CatalogOperations
{
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}"; public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}";
}
public class BasketOperations
{
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
public static string UpdateBasket() => "/api/v1/basket";
}
public class OrdersOperations
{
public static string GetOrderDraft() => "/api/v1/orders/draft";
}
public string Basket { get; set; }
public string Catalog { get; set; }
public string Orders { get; set; }
public string GrpcBasket { get; set; }
public string GrpcCatalog { get; set; }
public string GrpcOrdering { get; set; }
} }
public class BasketOperations
{
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
public static string UpdateBasket() => "/api/v1/basket";
}
public class OrdersOperations
{
public static string GetOrderDraft() => "/api/v1/orders/draft";
}
public string Basket { get; set; }
public string Catalog { get; set; }
public string Orders { get; set; }
public string GrpcBasket { get; set; }
public string GrpcCatalog { get; set; }
public string GrpcOrdering { get; set; }
} }

View File

@ -1,143 +1,156 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
[Route("api/v1/[controller]")] namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[Authorize]
[ApiController]
public class BasketController : ControllerBase
{ {
private readonly ICatalogService _catalog; [Route("api/v1/[controller]")]
private readonly IBasketService _basket; [Authorize]
[ApiController]
public BasketController(ICatalogService catalogService, IBasketService basketService) public class BasketController : ControllerBase
{ {
_catalog = catalogService; private readonly ICatalogService _catalog;
_basket = basketService; private readonly IBasketService _basket;
}
[HttpPost] public BasketController(ICatalogService catalogService, IBasketService basketService)
[HttpPut]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{ {
return BadRequest("Need to pass at least one basket line"); _catalog = catalogService;
_basket = basketService;
} }
// Retrieve the current basket [HttpPost]
var basket = await _basket.GetByIdAsync(data.BuyerId) ?? new BasketData(data.BuyerId); [HttpPut]
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId)); [ProducesResponseType((int)HttpStatusCode.BadRequest)]
// group by product id to avoid duplicates [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
var itemsCalculated = data public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
.Items {
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i }) if (data.Items == null || !data.Items.Any())
.Select(groupedItem => {
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId);
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
// group by product id to avoid duplicates
var itemsCalculated = data
.Items
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i })
.Select(groupedItem =>
{
var item = groupedItem.items.First();
item.Quantity = groupedItem.items.Sum(i => i.Quantity);
return item;
});
foreach (var bitem in itemsCalculated)
{
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
{ {
var item = groupedItem.items.First(); return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
item.Quantity = groupedItem.items.Sum(i => i.Quantity); }
return item;
});
foreach (var bitem in itemsCalculated) var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId);
{ if (itemInBasket == null)
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
{
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
}
var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId);
if (itemInBasket == null)
{
basket.Items.Add(new BasketDataItem()
{ {
Id = bitem.Id, basket.Items.Add(new BasketDataItem()
ProductId = catalogItem.Id, {
ProductName = catalogItem.Name, Id = bitem.Id,
PictureUrl = catalogItem.PictureUri, ProductId = catalogItem.Id,
UnitPrice = catalogItem.Price, ProductName = catalogItem.Name,
Quantity = bitem.Quantity PictureUrl = catalogItem.PictureUri,
}); UnitPrice = catalogItem.Price,
Quantity = bitem.Quantity
});
}
else
{
itemInBasket.Quantity = bitem.Quantity;
}
} }
else
await _basket.UpdateAsync(basket);
return basket;
}
[HttpPut]
[Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{ {
itemInBasket.Quantity = bitem.Quantity; return BadRequest("No updates sent");
} }
}
await _basket.UpdateAsync(basket); // Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
return basket; if (currentBasket == null)
}
[HttpPut]
[Route("items")]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
return BadRequest("No updates sent");
}
// Retrieve the current basket
var currentBasket = await _basket.GetByIdAsync(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
}
// Update with new quantities
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{ {
return BadRequest($"Basket item with id {update.BasketItemId} not found"); return BadRequest($"Basket with id {data.BasketId} not found.");
} }
basketItem.Quantity = update.NewQty; // Update with new quantities
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
await _basket.UpdateAsync(currentBasket);
return currentBasket;
} }
// Save the updated basket [HttpPost]
await _basket.UpdateAsync(currentBasket); [Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
return currentBasket; [ProducesResponseType((int)HttpStatusCode.OK)]
} public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
[HttpPost]
[Route("items")]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{ {
return BadRequest("Invalid payload"); if (data == null || data.Quantity == 0)
{
return BadRequest("Invalid payload");
}
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id,
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
// Step 4: Update basket
await _basket.UpdateAsync(currentBasket);
return Ok();
} }
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetByIdAsync(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id,
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
// Step 4: Update basket
await _basket.UpdateAsync(currentBasket);
return Ok();
} }
} }

View File

@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
{
[Route("")]
public class HomeController : Controller
{
[HttpGet()]
public IActionResult Index()
{
return new RedirectResult("~/swagger");
}
}
}

View File

@ -1,36 +1,45 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System.Net;
using System.Threading.Tasks;
[Route("api/v1/[controller]")] namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[Authorize]
[ApiController]
public class OrderController : ControllerBase
{ {
private readonly IBasketService _basketService; [Route("api/v1/[controller]")]
private readonly IOrderingService _orderingService; [Authorize]
[ApiController]
public OrderController(IBasketService basketService, IOrderingService orderingService) public class OrderController : ControllerBase
{ {
_basketService = basketService; private readonly IBasketService _basketService;
_orderingService = orderingService; private readonly IOrderingService _orderingService;
}
[Route("draft/{basketId}")] public OrderController(IBasketService basketService, IOrderingService orderingService)
[HttpGet]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{ {
return BadRequest("Need a valid basketid"); _basketService = basketService;
} _orderingService = orderingService;
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetByIdAsync(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
} }
return await _orderingService.GetOrderDraftAsync(basket); [Route("draft/{basketId}")]
[HttpGet]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
return await _orderingService.GetOrderDraftAsync(basket);
}
} }
} }

View File

@ -1,8 +1,8 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base FROM mcr.microsoft.com/dotnet/nightly/aspnet:6.0.0-preview.7 AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build FROM mcr.microsoft.com/dotnet/nightly/sdk:6.0.100-preview.7 AS build
WORKDIR /src WORKDIR /src
# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles # It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles
@ -11,6 +11,7 @@ COPY "eShopOnContainers-ServicesAndWebApps.sln" "eShopOnContainers-ServicesAndWe
COPY "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" COPY "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj" "ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj"
COPY "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" COPY "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj" "ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj"
COPY "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj" "BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj"
COPY "BuildingBlocks/EventBus/EventBus/EventBus.csproj" "BuildingBlocks/EventBus/EventBus/EventBus.csproj" COPY "BuildingBlocks/EventBus/EventBus/EventBus.csproj" "BuildingBlocks/EventBus/EventBus/EventBus.csproj"
COPY "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj" "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj" COPY "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj" "BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj"
COPY "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" COPY "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj" "BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj"
@ -32,8 +33,6 @@ COPY "Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj"
COPY "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" COPY "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj" "Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj"
COPY "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" COPY "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj" "Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj"
COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment.API/Payment.API.csproj" COPY "Services/Payment/Payment.API/Payment.API.csproj" "Services/Payment/Payment.API/Payment.API.csproj"
COPY "Services/Services.Common/Services.Common.csproj" "Services/Services.Common/Services.Common.csproj"
COPY "Services/Contact/Contact.API/Contact.API.csproj" "Services/Contact/Contact.API/Contact.API.csproj"
COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" COPY "Services/Webhooks/Webhooks.API/Webhooks.API.csproj" "Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" COPY "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj" "Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj"
COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj" COPY "Web/WebhookClient/WebhookClient.csproj" "Web/WebhookClient/WebhookClient.csproj"
@ -43,7 +42,6 @@ COPY "Web/WebStatus/WebStatus.csproj" "Web/WebStatus/WebStatus.csproj"
COPY "docker-compose.dcproj" "docker-compose.dcproj" COPY "docker-compose.dcproj" "docker-compose.dcproj"
COPY "Directory.Packages.props" "Directory.Packages.props"
COPY "NuGet.config" "NuGet.config" COPY "NuGet.config" "NuGet.config"
RUN dotnet restore "eShopOnContainers-ServicesAndWebApps.sln" RUN dotnet restore "eShopOnContainers-ServicesAndWebApps.sln"

View File

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:7.0 FROM mcr.microsoft.com/dotnet/nightly/sdk:6.0.100-preview.6
ARG BUILD_CONFIGURATION=Debug ARG BUILD_CONFIGURATION=Debug
ENV ASPNETCORE_ENVIRONMENT=Development ENV ASPNETCORE_ENVIRONMENT=Development
ENV DOTNET_USE_POLLING_FILE_WATCHER=true ENV DOTNET_USE_POLLING_FILE_WATCHER=true
@ -6,6 +6,7 @@ EXPOSE 80
WORKDIR /src WORKDIR /src
COPY ["src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj", "src/ApiGateways/Mobile.Bff.Shopping/aggregator/"] COPY ["src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj", "src/ApiGateways/Mobile.Bff.Shopping/aggregator/"]
COPY ["src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj", "src/BuildingBlocks/Devspaces.Support/"]
COPY ["src/NuGet.config", "src/NuGet.config"] COPY ["src/NuGet.config", "src/NuGet.config"]
RUN dotnet restore src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj -nowarn:msb3202,nu1503 RUN dotnet restore src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj -nowarn:msb3202,nu1503

View File

@ -1,62 +0,0 @@
internal static class Extensions
{
public static IServiceCollection AddReverseProxy(this IServiceCollection services, IConfiguration configuration)
{
services.AddReverseProxy().LoadFromConfig(configuration.GetRequiredSection("ReverseProxy"));
return services;
}
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks()
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("CatalogUrlHC")), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("OrderingUrlHC")), name: "orderingapi-check", tags: new string[] { "orderingapi" })
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("BasketUrlHC")), name: "basketapi-check", tags: new string[] { "basketapi" })
.AddUrlGroup(_ => new Uri(configuration.GetRequiredValue("IdentityUrlHC")), name: "identityapi-check", tags: new string[] { "identityapi" });
return services;
}
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
// Register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
// Register http services
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>();
return services;
}
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
{
services.AddTransient<GrpcExceptionInterceptor>();
services.AddScoped<IBasketService, BasketService>();
services.AddGrpcClient<Basket.BasketClient>((services, options) =>
{
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket;
options.Address = new Uri(basketApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
services.AddScoped<ICatalogService, CatalogService>();
services.AddGrpcClient<Catalog.CatalogClient>((services, options) =>
{
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog;
options.Address = new Uri(catalogApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
services.AddScoped<IOrderingService, OrderingService>();
services.AddGrpcClient<GrpcOrdering.OrderingGrpc.OrderingGrpcClient>((services, options) =>
{
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
options.Address = new Uri(orderingApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
return services;
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters
{
namespace Basket.API.Infrastructure.Filters
{
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// Check for authorize attribute
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() ||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
if (!hasAuthorize) return;
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
var oAuthScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
};
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[ oAuthScheme ] = new [] { "Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator" }
}
};
}
}
}
}

View File

@ -1,13 +0,0 @@
global using System.Text.Json;
global using CatalogApi;
global using Grpc.Core;
global using Grpc.Core.Interceptors;
global using GrpcBasket;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
global using Microsoft.Extensions.Options;
global using Services.Common;

View File

@ -1,35 +1,41 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure; using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
public class GrpcExceptionInterceptor : Interceptor namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure
{ {
private readonly ILogger<GrpcExceptionInterceptor> _logger; public class GrpcExceptionInterceptor : Interceptor
public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
{ {
_logger = logger; private readonly ILogger<GrpcExceptionInterceptor> _logger;
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>( public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
}
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
{
try
{ {
var response = await t; _logger = logger;
return response;
} }
catch (RpcException e)
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{ {
_logger.LogError(e, "Error calling via gRPC: {Status}", e.Status); var call = continuation(request, context);
return default;
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
}
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
{
try
{
var response = await t;
return response;
}
catch (RpcException e)
{
_logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message);
return default;
}
} }
} }
} }

View File

@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure
{
public class HttpClientAuthorizationDelegatingHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<HttpClientAuthorizationDelegatingHandler> _logger;
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor, ILogger<HttpClientAuthorizationDelegatingHandler> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Version = new System.Version(2, 0);
request.Method = HttpMethod.Get;
var authorizationHeader = _httpContextAccessor.HttpContext
.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authorizationHeader))
{
request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
}
var token = await GetToken();
if (token != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
return await base.SendAsync(request, cancellationToken);
}
async Task<string> GetToken()
{
const string ACCESS_TOKEN = "access_token";
return await _httpContextAccessor.HttpContext
.GetTokenAsync(ACCESS_TOKEN);
}
}
}

View File

@ -1,23 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyName>Mobile.Shopping.HttpAggregator</AssemblyName> <AssemblyName>Mobile.Shopping.HttpAggregator</AssemblyName>
<RootNamespace>Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator</RootNamespace> <RootNamespace>Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator</RootNamespace>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<ImplicitUsings>enable</ImplicitUsings> <GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
<LangVersion>preview</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Yarp.ReverseProxy" /> <Folder Include="wwwroot\" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" />
<PackageReference Include="Google.Protobuf" />
<PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" />
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\Services\Services.Common\Services.Common.csproj" /> <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="5.0.1" />
<PackageReference Include="Google.Protobuf" Version="3.14.0" />
<PackageReference Include="Grpc.AspNetCore.Server.ClientFactory" Version="2.34.0" />
<PackageReference Include="Grpc.Core" Version="2.34.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.34.0" />
<PackageReference Include="Grpc.Tools" Version="2.34.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0-dev-00834" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\Devspaces.Support\Devspaces.Support.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,15 +1,16 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class AddBasketItemRequest
{ {
public int CatalogItemId { get; set; } public class AddBasketItemRequest
public string BasketId { get; set; }
public int Quantity { get; set; }
public AddBasketItemRequest()
{ {
Quantity = 1; public int CatalogItemId { get; set; }
public string BasketId { get; set; }
public int Quantity { get; set; }
public AddBasketItemRequest()
{
Quantity = 1;
}
} }
} }

View File

@ -1,17 +1,22 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using System.Collections.Generic;
public class BasketData namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{ {
public string BuyerId { get; set; }
public List<BasketDataItem> Items { get; set; } = new(); public class BasketData
public BasketData()
{ {
public string BuyerId { get; set; }
public List<BasketDataItem> Items { get; set; } = new List<BasketDataItem>();
public BasketData()
{
}
public BasketData(string buyerId)
{
BuyerId = buyerId;
}
} }
public BasketData(string buyerId)
{
BuyerId = buyerId;
}
} }

View File

@ -1,18 +1,21 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class BasketDataItem
{ {
public string Id { get; set; }
public int ProductId { get; set; } public class BasketDataItem
{
public string Id { get; set; }
public string ProductName { get; set; } public int ProductId { get; set; }
public decimal UnitPrice { get; set; } public string ProductName { get; set; }
public decimal OldUnitPrice { get; set; } public decimal UnitPrice { get; set; }
public int Quantity { get; set; } public decimal OldUnitPrice { get; set; }
public int Quantity { get; set; }
public string PictureUrl { get; set; }
}
public string PictureUrl { get; set; }
} }

View File

@ -1,12 +1,13 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class CatalogItem
{ {
public int Id { get; set; } public class CatalogItem
{
public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public decimal Price { get; set; } public decimal Price { get; set; }
public string PictureUri { get; set; } public string PictureUri { get; set; }
}
} }

View File

@ -1,42 +1,48 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using System;
using System.Collections.Generic;
public class OrderData namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{ {
public string OrderNumber { get; set; }
public DateTime Date { get; set; } public class OrderData
{
public string OrderNumber { get; set; }
public string Status { get; set; } public DateTime Date { get; set; }
public decimal Total { get; set; } public string Status { get; set; }
public string Description { get; set; } public decimal Total { get; set; }
public string City { get; set; } public string Description { get; set; }
public string Street { get; set; } public string City { get; set; }
public string State { get; set; } public string Street { get; set; }
public string Country { get; set; } public string State { get; set; }
public string ZipCode { get; set; } public string Country { get; set; }
public string CardNumber { get; set; } public string ZipCode { get; set; }
public string CardHolderName { get; set; } public string CardNumber { get; set; }
public bool IsDraft { get; set; } public string CardHolderName { get; set; }
public DateTime CardExpiration { get; set; } public bool IsDraft { get; set; }
public string CardExpirationShort { get; set; } public DateTime CardExpiration { get; set; }
public string CardSecurityNumber { get; set; } public string CardExpirationShort { get; set; }
public int CardTypeId { get; set; } public string CardSecurityNumber { get; set; }
public string Buyer { get; set; } public int CardTypeId { get; set; }
public string Buyer { get; set; }
public List<OrderItemData> OrderItems { get; } = new List<OrderItemData>();
}
public List<OrderItemData> OrderItems { get; } = new();
} }

View File

@ -1,16 +1,19 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class OrderItemData
{ {
public int ProductId { get; set; }
public string ProductName { get; set; } public class OrderItemData
{
public int ProductId { get; set; }
public decimal UnitPrice { get; set; } public string ProductName { get; set; }
public decimal Discount { get; set; } public decimal UnitPrice { get; set; }
public int Units { get; set; } public decimal Discount { get; set; }
public int Units { get; set; }
public string PictureUrl { get; set; }
}
public string PictureUrl { get; set; }
} }

View File

@ -1,13 +1,16 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class UpdateBasketItemData
{ {
public string BasketItemId { get; set; }
public int NewQty { get; set; } public class UpdateBasketItemData
public UpdateBasketItemData()
{ {
NewQty = 0; public string BasketItemId { get; set; }
public int NewQty { get; set; }
public UpdateBasketItemData()
{
NewQty = 0;
}
} }
} }

View File

@ -1,13 +1,19 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using System.Collections.Generic;
public class UpdateBasketItemsRequest namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{ {
public string BasketId { get; set; }
public ICollection<UpdateBasketItemData> Updates { get; set; } public class UpdateBasketItemsRequest
public UpdateBasketItemsRequest()
{ {
Updates = new List<UpdateBasketItemData>();
public string BasketId { get; set; }
public ICollection<UpdateBasketItemData> Updates { get; set; }
public UpdateBasketItemsRequest()
{
Updates = new List<UpdateBasketItemData>();
}
} }
} }

View File

@ -1,8 +1,13 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using System.Collections.Generic;
public class UpdateBasketRequest namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{ {
public string BuyerId { get; set; }
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; } public class UpdateBasketRequest
{
public string BuyerId { get; set; }
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
}
} }

View File

@ -1,10 +1,13 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class UpdateBasketRequestItemData
{ {
public string Id { get; set; } // Basket id
public int ProductId { get; set; } // Catalog item id public class UpdateBasketRequestItemData
{
public string Id { get; set; } // Basket id
public int ProductId { get; set; } // Catalog item id
public int Quantity { get; set; } // Quantity
}
public int Quantity { get; set; } // Quantity
} }

View File

@ -1,24 +1,29 @@
var builder = WebApplication.CreateBuilder(args); using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator;
using Serilog;
builder.AddServiceDefaults();
builder.Services.AddReverseProxy(builder.Configuration); BuildWebHost(args).Run();
builder.Services.AddControllers(); IWebHost BuildWebHost(string[] args) =>
WebHost
builder.Services.AddHealthChecks(builder.Configuration); .CreateDefaultBuilder(args)
.ConfigureAppConfiguration(cb =>
builder.Services.AddApplicationServices(); {
builder.Services.AddGrpcServices(); var sources = cb.Sources;
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
builder.Services.Configure<UrlsConfig>(builder.Configuration.GetSection("urls")); {
Optional = true,
var app = builder.Build(); Path = "appsettings.localhost.json",
ReloadOnChange = false
app.UseServiceDefaults(); });
})
app.UseHttpsRedirection(); .UseStartup<Startup>()
.UseSerilog((builderContext, config) =>
app.MapControllers(); {
app.MapReverseProxy(); config
.MinimumLevel.Information()
await app.RunAsync(); .Enrich.FromLogContext()
.WriteTo.Console();
})
.Build();

View File

@ -24,6 +24,13 @@
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"applicationUrl": "http://localhost:61632/" "applicationUrl": "http://localhost:61632/"
},
"Azure Dev Spaces": {
"commandName": "AzureDevSpaces",
"launchBrowser": true,
"resourceGroup": "eshoptestedu",
"aksName": "eshoptestedu",
"subscriptionId": "e3035ac1-c06c-4daf-8939-57b3c5f1f759"
} }
} }
} }

View File

@ -1,83 +1,90 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using GrpcBasket;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;
public class BasketService : IBasketService namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
private readonly Basket.BasketClient _basketClient; public class BasketService : IBasketService
private readonly ILogger<BasketService> _logger;
public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
{ {
_basketClient = basketClient; private readonly Basket.BasketClient _basketClient;
_logger = logger; private readonly ILogger<BasketService> _logger;
}
public async Task<BasketData> GetByIdAsync(string id) public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
{
_logger.LogDebug("grpc client created, request = {@id}", id);
var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
_logger.LogDebug("grpc response {@response}", response);
return MapToBasketData(response);
}
public async Task UpdateAsync(BasketData currentBasket)
{
_logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
var request = MapToCustomerBasketRequest(currentBasket);
_logger.LogDebug("Grpc update basket request {@request}", request);
await _basketClient.UpdateBasketAsync(request);
}
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)
{
if (customerBasketRequest == null)
{ {
return null; _basketClient = basketClient;
_logger = logger;
} }
var map = new BasketData public async Task<BasketData> GetById(string id)
{ {
BuyerId = customerBasketRequest.Buyerid _logger.LogDebug("grpc client created, request = {@id}", id);
}; var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
_logger.LogDebug("grpc response {@response}", response);
customerBasketRequest.Items.ToList().ForEach(item => map.Items.Add(new BasketDataItem return MapToBasketData(response);
{
Id = item.Id,
OldUnitPrice = (decimal)item.Oldunitprice,
PictureUrl = item.Pictureurl,
ProductId = item.Productid,
ProductName = item.Productname,
Quantity = item.Quantity,
UnitPrice = (decimal)item.Unitprice
}));
return map;
}
private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData)
{
if (basketData == null)
{
return null;
} }
var map = new CustomerBasketRequest public async Task UpdateAsync(BasketData currentBasket)
{ {
Buyerid = basketData.BuyerId _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
}; var request = MapToCustomerBasketRequest(currentBasket);
_logger.LogDebug("Grpc update basket request {@request}", request);
basketData.Items.ToList().ForEach(item => map.Items.Add(new BasketItemResponse await _basketClient.UpdateBasketAsync(request);
}
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)
{ {
Id = item.Id, if (customerBasketRequest == null)
Oldunitprice = (double)item.OldUnitPrice, {
Pictureurl = item.PictureUrl, return null;
Productid = item.ProductId, }
Productname = item.ProductName,
Quantity = item.Quantity,
Unitprice = (double)item.UnitPrice
}));
return map; var map = new BasketData
{
BuyerId = customerBasketRequest.Buyerid
};
customerBasketRequest.Items.ToList().ForEach(item => map.Items.Add(new BasketDataItem
{
Id = item.Id,
OldUnitPrice = (decimal)item.Oldunitprice,
PictureUrl = item.Pictureurl,
ProductId = item.Productid,
ProductName = item.Productname,
Quantity = item.Quantity,
UnitPrice = (decimal)item.Unitprice
}));
return map;
}
private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData)
{
if (basketData == null)
{
return null;
}
var map = new CustomerBasketRequest
{
Buyerid = basketData.BuyerId
};
basketData.Items.ToList().ForEach(item => map.Items.Add(new BasketItemResponse
{
Id = item.Id,
Oldunitprice = (double)item.OldUnitPrice,
Pictureurl = item.PictureUrl,
Productid = item.ProductId,
Productname = item.ProductName,
Quantity = item.Quantity,
Unitprice = (double)item.UnitPrice
}));
return map;
}
} }
} }

View File

@ -1,36 +1,43 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using CatalogApi;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class CatalogService : ICatalogService namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
private readonly Catalog.CatalogClient _client; public class CatalogService : ICatalogService
public CatalogService(Catalog.CatalogClient client)
{ {
_client = client; private readonly Catalog.CatalogClient _client;
}
public async Task<CatalogItem> GetCatalogItemAsync(int id) public CatalogService(Catalog.CatalogClient client)
{
var request = new CatalogItemRequest { Id = id };
var response = await _client.GetItemByIdAsync(request);
return MapToCatalogItemResponse(response);
}
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
{
var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 };
var response = await _client.GetItemsByIdsAsync(request);
return response.Data.Select(MapToCatalogItemResponse);
}
private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse)
{
return new CatalogItem
{ {
Id = catalogItemResponse.Id, _client = client;
Name = catalogItemResponse.Name, }
PictureUri = catalogItemResponse.PictureUri,
Price = (decimal)catalogItemResponse.Price public async Task<CatalogItem> GetCatalogItemAsync(int id)
}; {
var request = new CatalogItemRequest { Id = id };
var response = await _client.GetItemByIdAsync(request);
return MapToCatalogItemResponse(response);
}
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
{
var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 };
var response = await _client.GetItemsByIdsAsync(request);
return response.Data.Select(MapToCatalogItemResponse);
}
private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse)
{
return new CatalogItem
{
Id = catalogItemResponse.Id,
Name = catalogItemResponse.Name,
PictureUri = catalogItemResponse.PictureUri,
Price = (decimal)catalogItemResponse.Price
};
}
} }
} }

View File

@ -1,8 +1,13 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Threading.Tasks;
public interface IBasketService namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
Task<BasketData> GetByIdAsync(string id); public interface IBasketService
{
Task<BasketData> GetById(string id);
Task UpdateAsync(BasketData currentBasket); Task UpdateAsync(BasketData currentBasket);
}
} }

View File

@ -1,8 +1,13 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
public interface ICatalogService namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
Task<CatalogItem> GetCatalogItemAsync(int id); public interface ICatalogService
{
Task<CatalogItem> GetCatalogItemAsync(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids); Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids);
}
} }

View File

@ -1,6 +1,10 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Threading.Tasks;
public interface IOrderApiClient namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket); public interface IOrderApiClient
{
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket);
}
} }

View File

@ -1,6 +1,10 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Threading.Tasks;
public interface IOrderingService namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
Task<OrderData> GetOrderDraftAsync(BasketData basketData); public interface IOrderingService
{
Task<OrderData> GetOrderDraftAsync(BasketData basketData);
}
} }

View File

@ -1,28 +1,40 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;
public class OrderApiClient : IOrderApiClient namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
private readonly HttpClient _apiClient; public class OrderApiClient : IOrderApiClient
private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls;
public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
{ {
_apiClient = httpClient; private readonly HttpClient _apiClient;
_logger = logger; private readonly ILogger<OrderApiClient> _logger;
_urls = config.Value; private readonly UrlsConfig _urls;
}
public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket) public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
{ {
var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); _apiClient = httpClient;
var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); _logger = logger;
var response = await _apiClient.PostAsync(uri, content); _urls = config.Value;
}
response.EnsureSuccessStatusCode(); public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket)
{
var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(uri, content);
var ordersDraftResponse = await response.Content.ReadAsStringAsync(); response.EnsureSuccessStatusCode();
return JsonSerializer.Deserialize<OrderData>(ordersDraftResponse, JsonDefaults.CaseInsensitiveOptions); var ordersDraftResponse = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<OrderData>(ordersDraftResponse, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
}
} }
} }

View File

@ -1,72 +1,79 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; using GrpcOrdering;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;
public class OrderingService : IOrderingService namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
private readonly GrpcOrdering.OrderingGrpc.OrderingGrpcClient _orderingGrpcClient; public class OrderingService : IOrderingService
private readonly ILogger<OrderingService> _logger;
public OrderingService(GrpcOrdering.OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
{ {
_orderingGrpcClient = orderingGrpcClient; private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
_logger = logger; private readonly ILogger<OrderingService> _logger;
}
public async Task<OrderData> GetOrderDraftAsync(BasketData basketData) public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
{
_logger.LogDebug(" grpc client created, basketData={@basketData}", basketData);
var command = MapToOrderDraftCommand(basketData);
var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command);
_logger.LogDebug(" grpc response: {@response}", response);
return MapToResponse(response, basketData);
}
private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData)
{
if (orderDraft == null)
{ {
return null; _orderingGrpcClient = orderingGrpcClient;
_logger = logger;
} }
var data = new OrderData public async Task<OrderData> GetOrderDraftAsync(BasketData basketData)
{ {
Buyer = basketData.BuyerId, _logger.LogDebug(" grpc client created, basketData={@basketData}", basketData);
Total = (decimal)orderDraft.Total,
};
orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData var command = MapToOrderDraftCommand(basketData);
var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command);
_logger.LogDebug(" grpc response: {@response}", response);
return MapToResponse(response, basketData);
}
private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData)
{ {
Discount = (decimal)o.Discount, if (orderDraft == null)
PictureUrl = o.PictureUrl, {
ProductId = o.ProductId, return null;
ProductName = o.ProductName, }
UnitPrice = (decimal)o.UnitPrice,
Units = o.Units, var data = new OrderData
})); {
Buyer = basketData.BuyerId,
Total = (decimal)orderDraft.Total,
};
orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData
{
Discount = (decimal)o.Discount,
PictureUrl = o.PictureUrl,
ProductId = o.ProductId,
ProductName = o.ProductName,
UnitPrice = (decimal)o.UnitPrice,
Units = o.Units,
}));
return data;
}
private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
{
var command = new CreateOrderDraftCommand
{
BuyerId = basketData.BuyerId,
};
basketData.Items.ForEach(i => command.Items.Add(new BasketItem
{
Id = i.Id,
OldUnitPrice = (double)i.OldUnitPrice,
PictureUrl = i.PictureUrl,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = (double)i.UnitPrice,
}));
return command;
}
return data;
} }
private GrpcOrdering.CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
{
var command = new GrpcOrdering.CreateOrderDraftCommand
{
BuyerId = basketData.BuyerId,
};
basketData.Items.ForEach(i => command.Items.Add(new GrpcOrdering.BasketItem
{
Id = i.Id,
OldUnitPrice = (double)i.OldUnitPrice,
PictureUrl = i.PictureUrl,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = (double)i.UnitPrice,
}));
return command;
}
} }

View File

@ -0,0 +1,222 @@
using CatalogApi;
using Devspaces.Support;
using GrpcBasket;
using GrpcOrdering;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" });
services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration)
.AddDevspaces()
.AddHttpServices()
.AddGrpcServices();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.OAuthClientId("mobileshoppingaggswaggerui");
c.OAuthClientSecret(string.Empty);
c.OAuthRealm(string.Empty);
c.OAuthAppName("Purchase BFF Swagger UI");
});
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
});
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
services.AddControllers()
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Shopping Aggregator for Mobile Clients",
Version = "v1",
Description = "Shopping Aggregator for Mobile Clients"
});
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{
Implicit = new OpenApiOAuthFlow()
{
AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"),
TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"),
Scopes = new Dictionary<string, string>()
{
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" }
}
}
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
var identityUrl = configuration.GetValue<string>("urls:identity");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "mobileshoppingagg";
});
return services;
}
public static IServiceCollection AddHttpServices(this IServiceCollection services)
{
//register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//register http services
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
.AddDevspacesSupport();
return services;
}
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
{
services.AddTransient<GrpcExceptionInterceptor>();
services.AddScoped<IBasketService, BasketService>();
services.AddGrpcClient<Basket.BasketClient>((services, options) =>
{
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket;
options.Address = new Uri(basketApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
services.AddScoped<ICatalogService, CatalogService>();
services.AddGrpcClient<Catalog.CatalogClient>((services, options) =>
{
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog;
options.Address = new Uri(catalogApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
services.AddScoped<IOrderingService, OrderingService>();
services.AddGrpcClient<OrderingGrpc.OrderingGrpcClient>((services, options) =>
{
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
options.Address = new Uri(orderingApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
return services;
}
}
}

View File

@ -1,138 +1,15 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "IncludeScopes": false,
"Default": "Information", "Debug": {
"Microsoft.AspNetCore": "Warning", "LogLevel": {
"System.Net.Http": "Warning" "Default": "Warning"
}
},
"OpenApi": {
"Endpoint": {
"Name": "Purchase BFF V1"
},
"Document": {
"Description": "Shopping Aggregator for Mobile Clients",
"Title": "Shopping Aggregator for Mobile Clients",
"Version": "v1"
},
"Auth": {
"ClientId": "mobileshoppingaggswaggerui",
"AppName": "Mobile shopping BFF Swagger UI"
}
},
"Identity": {
"Url": "http://localhost:5223",
"Audience": "mobileshoppingagg",
"Scopes": {
"webshoppingagg": "Shopping Aggregator for Mobile Clients"
}
},
"ReverseProxy": {
"Routes": {
"c-short": {
"ClusterId": "catalog",
"Match": {
"Path": "c/{**catch-all}"
},
"Transforms": [
{ "PathRemovePrefix": "/c" }
]
},
"c-long": {
"ClusterId": "catalog",
"Match": {
"Path": "catalog-api/{**catch-all}"
},
"Transforms": [
{ "PathRemovePrefix": "/catalog-api" }
]
},
"b-short": {
"ClusterId": "basket",
"Match": {
"Path": "b/{**catch-all}"
},
"Transforms": [
{ "PathRemovePrefix": "/b" }
]
},
"b-long": {
"ClusterId": "basket",
"Match": {
"Path": "basket-api/{**catch-all}"
},
"Transforms": [
{ "PathRemovePrefix": "/basket-api" }
]
},
"o-short": {
"ClusterId": "orders",
"Match": {
"Path": "o/{**catch-all}"
},
"Transforms": [
{ "PathRemovePrefix": "/o" }
]
},
"o-long": {
"ClusterId": "orders",
"Match": {
"Path": "ordering-api/{**catch-all}"
},
"Transforms": [
{ "PathRemovePrefix": "/ordering-api" }
]
},
"h-long": {
"ClusterId": "signalr",
"Match": {
"Path": "hub/notificationhub/{**catch-all}"
}
} }
}, },
"Clusters": { "Console": {
"basket": { "LogLevel": {
"Destinations": { "Default": "Warning"
"destination0": {
"Address": "http://localhost:5221"
}
}
},
"catalog": {
"Destinations": {
"destination0": {
"Address": "http://localhost:5222"
}
}
},
"orders": {
"Destinations": {
"destination0": {
"Address": "http://localhost:5224"
}
}
},
"signalr": {
"Destinations": {
"destination0": {
"Address": "http://localhost:5225"
}
}
} }
} }
}, }
"Urls": {
"Basket": "http://localhost:5221",
"Catalog": "http://localhost:5222",
"Orders": "http://localhost:5224",
"Identity": "http://localhost:5223",
"Signalr": "http://localhost:5225",
"GrpcBasket": "http://localhost:6221",
"GrpcCatalog": "http://localhost:6222",
"GrpcOrdering": "http://localhost:6224"
},
"CatalogUrlHC": "http://localhost:5222/hc",
"OrderingUrlHC": "http://localhost:5224/hc",
"BasketUrlHC": "http://localhost:5221/hc",
"IdentityUrlHC": "http://localhost:5223/hc"
} }

View File

@ -8,19 +8,16 @@
"grpcCatalog": "http://localhost:81", "grpcCatalog": "http://localhost:81",
"grpcOrdering": "http://localhost:5581" "grpcOrdering": "http://localhost:5581"
}, },
"Identity": { "IdentityUrlExternal": "http://localhost:5105",
"ExternalUrl": "http://localhost:5105", "IdentityUrl": "http://localhost:5105",
"Url": "http://localhost:5105",
},
"Logging": { "Logging": {
"IncludeScopes": false,
"Debug": { "Debug": {
"IncludeScopes": false,
"LogLevel": { "LogLevel": {
"Default": "Debug" "Default": "Debug"
} }
}, },
"Console": { "Console": {
"IncludeScopes": false,
"LogLevel": { "LogLevel": {
"Default": "Debug" "Default": "Debug"
} }

View File

@ -0,0 +1,55 @@
kind: helm-release
apiVersion: 1.1
build:
context: ..\..\..\..
dockerfile: Dockerfile
install:
chart: ../../../../k8s/helm/mobileshoppingagg
set:
image:
tag: $(tag)
pullPolicy: Never
ingress:
annotations:
kubernetes.io/ingress.class: traefik-azds
hosts:
# This expands to [space.s.]apigwms.<guid>.<region>.aksapp.io
- $(spacePrefix)eshop$(hostSuffix)
inf:
k8s:
dns: $(spacePrefix)eshop$(hostSuffix)
values:
- values.dev.yaml?
- secrets.dev.yaml?
- app.yaml
- inf.yaml
configurations:
develop:
build:
useGitIgnore: true
dockerfile: Dockerfile.develop
container:
syncTarget: /src
sync:
- '**/Pages/**'
- '**/Views/**'
- '**/wwwroot/**'
- '!**/*.{sln,csproj}'
command:
- dotnet
- run
- --no-restore
- --no-build
- --no-launch-profile
- -c
- ${Configuration:-Debug}
iterate:
processesToKill:
- dotnet
- vsdbg
buildCommands:
- - dotnet
- build
- --no-restore
- -c
- ${Configuration:-Debug}

View File

@ -1,41 +1,45 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; using System.Collections.Generic;
public class UrlsConfig namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config
{ {
public class CatalogOperations public class UrlsConfig
{ {
// grpc call under REST must go trough port 80
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
public static string GetItemById(string ids) => $"/api/v1/catalog/items/ids/{string.Join(',', ids)}"; public class CatalogOperations
{
// grpc call under REST must go trough port 80
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
// REST call standard must go through port 5000 public static string GetItemById(string ids) => $"/api/v1/catalog/items/ids/{string.Join(',', ids)}";
public static string GetItemsById(IEnumerable<int> ids) => $":5000/api/v1/catalog/items?ids={string.Join(',', ids)}";
// REST call standard must go through port 5000
public static string GetItemsById(IEnumerable<int> ids) => $":5000/api/v1/catalog/items?ids={string.Join(',', ids)}";
}
public class BasketOperations
{
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
public static string UpdateBasket() => "/api/v1/basket";
}
public class OrdersOperations
{
public static string GetOrderDraft() => "/api/v1/orders/draft";
}
public string Basket { get; set; }
public string Catalog { get; set; }
public string Orders { get; set; }
public string GrpcBasket { get; set; }
public string GrpcCatalog { get; set; }
public string GrpcOrdering { get; set; }
} }
public class BasketOperations
{
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
public static string UpdateBasket() => "/api/v1/basket";
}
public class OrdersOperations
{
public static string GetOrderDraft() => "/api/v1/orders/draft";
}
public string Basket { get; set; }
public string Catalog { get; set; }
public string Orders { get; set; }
public string GrpcBasket { get; set; }
public string GrpcCatalog { get; set; }
public string GrpcOrdering { get; set; }
} }

View File

@ -1,152 +1,164 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
[Route("api/v1/[controller]")] namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
[Authorize]
[ApiController]
public class BasketController : ControllerBase
{ {
private readonly ICatalogService _catalog; [Route("api/v1/[controller]")]
private readonly IBasketService _basket; [Authorize]
[ApiController]
public BasketController(ICatalogService catalogService, IBasketService basketService) public class BasketController : ControllerBase
{ {
_catalog = catalogService; private readonly ICatalogService _catalog;
_basket = basketService; private readonly IBasketService _basket;
}
[HttpPost] public BasketController(ICatalogService catalogService, IBasketService basketService)
[HttpPut]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{ {
return BadRequest("Need to pass at least one basket line"); _catalog = catalogService;
_basket = basketService;
} }
// Retrieve the current basket [HttpPost]
var basket = await _basket.GetByIdAsync(data.BuyerId) ?? new BasketData(data.BuyerId); [HttpPut]
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId)); [ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
// group by product id to avoid duplicates public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
var itemsCalculated = data
.Items
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i })
.Select(groupedItem =>
{
var item = groupedItem.items.First();
item.Quantity = groupedItem.items.Sum(i => i.Quantity);
return item;
});
foreach (var bitem in itemsCalculated)
{ {
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); if (data.Items == null || !data.Items.Any())
if (catalogItem == null)
{ {
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); return BadRequest("Need to pass at least one basket line");
} }
var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId); // Retrieve the current basket
if (itemInBasket == null) var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId);
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
// group by product id to avoid duplicates
var itemsCalculated = data
.Items
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i })
.Select(groupedItem =>
{
var item = groupedItem.items.First();
item.Quantity = groupedItem.items.Sum(i => i.Quantity);
return item;
});
foreach (var bitem in itemsCalculated)
{ {
basket.Items.Add(new BasketDataItem() var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
{ {
Id = bitem.Id, return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
ProductId = catalogItem.Id, }
ProductName = catalogItem.Name,
PictureUrl = catalogItem.PictureUri, var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId);
UnitPrice = catalogItem.Price, if (itemInBasket == null)
Quantity = bitem.Quantity {
}); basket.Items.Add(new BasketDataItem()
{
Id = bitem.Id,
ProductId = catalogItem.Id,
ProductName = catalogItem.Name,
PictureUrl = catalogItem.PictureUri,
UnitPrice = catalogItem.Price,
Quantity = bitem.Quantity
});
}
else
{
itemInBasket.Quantity = bitem.Quantity;
}
}
await _basket.UpdateAsync(basket);
return basket;
}
[HttpPut]
[Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
return BadRequest("No updates sent");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
}
// Update with new quantities
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
await _basket.UpdateAsync(currentBasket);
return currentBasket;
}
[HttpPost]
[Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
return BadRequest("Invalid payload");
}
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Search if exist product into basket
var product = currentBasket.Items.SingleOrDefault(i => i.ProductId == item.Id);
if (product != null)
{
// Step 4: Update quantity for product
product.Quantity += data.Quantity;
} }
else else
{ {
itemInBasket.Quantity = bitem.Quantity; // Step 4: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id,
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
} }
// Step 5: Update basket
await _basket.UpdateAsync(currentBasket);
return Ok();
} }
await _basket.UpdateAsync(basket);
return basket;
}
[HttpPut]
[Route("items")]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
return BadRequest("No updates sent");
}
// Retrieve the current basket
var currentBasket = await _basket.GetByIdAsync(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
}
// Update with new quantities
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
await _basket.UpdateAsync(currentBasket);
return currentBasket;
}
[HttpPost]
[Route("items")]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
return BadRequest("Invalid payload");
}
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetByIdAsync(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Search if exist product into basket
var product = currentBasket.Items.SingleOrDefault(i => i.ProductId == item.Id);
if (product != null)
{
// Step 4: Update quantity for product
product.Quantity += data.Quantity;
}
else
{
// Step 4: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id,
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
}
// Step 5: Update basket
await _basket.UpdateAsync(currentBasket);
return Ok();
} }
} }

View File

@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
{
[Route("")]
public class HomeController : Controller
{
[HttpGet()]
public IActionResult Index()
{
return new RedirectResult("~/swagger");
}
}
}

View File

@ -1,36 +1,44 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System.Net;
using System.Threading.Tasks;
[Route("api/v1/[controller]")] namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
[Authorize]
[ApiController]
public class OrderController : ControllerBase
{ {
private readonly IBasketService _basketService; [Route("api/v1/[controller]")]
private readonly IOrderingService _orderingService; [Authorize]
[ApiController]
public OrderController(IBasketService basketService, IOrderingService orderingService) public class OrderController : ControllerBase
{ {
_basketService = basketService; private readonly IBasketService _basketService;
_orderingService = orderingService; private readonly IOrderingService _orderingService;
} public OrderController(IBasketService basketService, IOrderingService orderingService)
[Route("draft/{basketId}")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrWhiteSpace(basketId))
{ {
return BadRequest("Need a valid basketid"); _basketService = basketService;
} _orderingService = orderingService;
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetByIdAsync(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
} }
return await _orderingService.GetOrderDraftAsync(basket); [Route("draft/{basketId}")]
[HttpGet]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
return await _orderingService.GetOrderDraftAsync(basket);
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More