Merge branch 'features/net21rc1' into dev

This commit is contained in:
eiximenis 2018-06-06 17:01:35 +02:00
commit c83c6842f5
97 changed files with 7438 additions and 10654 deletions

View File

@ -80,7 +80,6 @@ services:
- UseCustomizationData=True - UseCustomizationData=True
- AzureServiceBusEnabled=False - AzureServiceBusEnabled=False
- CheckUpdateTime=30000 - CheckUpdateTime=30000
- GracePeriodTime=1
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE} - OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False} - UseLoadTest=${USE_LOADTEST:-False}

View File

@ -85,7 +85,6 @@ services:
- UseCustomizationData=True - UseCustomizationData=True
- AzureServiceBusEnabled=False - AzureServiceBusEnabled=False
- CheckUpdateTime=30000 - CheckUpdateTime=30000
- GracePeriodTime=1
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE} - OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False} - UseLoadTest=${USE_LOADTEST:-False}

View File

@ -1,6 +1,25 @@
version: '3.4' version: '3.4'
services: services:
sql.data:
image: microsoft/mssql-server-linux:2017-latest
nosql.data:
image: mongo
basket.data:
image: redis:alpine
rabbitmq:
image: rabbitmq:3-management-alpine
identity.api:
image: eshop/identity.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Identity/Identity.API/Dockerfile
depends_on:
- sql.data
basket.api: basket.api:
image: eshop/basket.api:${TAG:-latest} image: eshop/basket.api:${TAG:-latest}
@ -21,14 +40,6 @@ services:
- sql.data - sql.data
- rabbitmq - rabbitmq
identity.api:
image: eshop/identity.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Identity/Identity.API/Dockerfile
depends_on:
- sql.data
ordering.api: ordering.api:
image: eshop/ordering.api:${TAG:-latest} image: eshop/ordering.api:${TAG:-latest}
build: build:
@ -58,36 +69,6 @@ services:
- identity.api - identity.api
- rabbitmq - rabbitmq
webspa:
image: eshop/webspa:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebSPA/Dockerfile
depends_on:
- catalog.api
- ordering.api
- identity.api
- basket.api
- marketing.api
webmvc:
image: eshop/webmvc:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebMVC/Dockerfile
depends_on:
- catalog.api
- ordering.api
- identity.api
- basket.api
- marketing.api
webstatus:
image: eshop/webstatus:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebStatus/Dockerfile
payment.api: payment.api:
image: eshop/payment.api:${TAG:-latest} image: eshop/payment.api:${TAG:-latest}
build: build:
@ -105,57 +86,135 @@ services:
- nosql.data - nosql.data
- rabbitmq - rabbitmq
sql.data:
image: microsoft/mssql-server-linux:2017-latest
nosql.data:
image: mongo
basket.data:
image: redis:alpine
rabbitmq:
image: rabbitmq:3-management-alpine
mobileshoppingapigw: mobileshoppingapigw:
image: eshop/ocelotapigw:${TAG:-latest} image: eshop/ocelotapigw:${TAG:-latest}
build: build:
context: . context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
depends_on:
- nosql.data
- sql.data
- identity.api
- rabbitmq
- ordering.api
- marketing.api
- catalog.api
- basket.api
mobilemarketingapigw: mobilemarketingapigw:
image: eshop/ocelotapigw:${TAG:-latest} image: eshop/ocelotapigw:${TAG:-latest}
build: build:
context: . context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
depends_on:
- nosql.data
- sql.data
- identity.api
- rabbitmq
- ordering.api
- marketing.api
- catalog.api
- basket.api
webshoppingapigw: webshoppingapigw:
image: eshop/ocelotapigw:${TAG:-latest} image: eshop/ocelotapigw:${TAG:-latest}
build: build:
context: . context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
depends_on:
- nosql.data
- sql.data
- identity.api
- rabbitmq
- ordering.api
- marketing.api
- catalog.api
- basket.api
webmarketingapigw: webmarketingapigw:
image: eshop/ocelotapigw:${TAG:-latest} image: eshop/ocelotapigw:${TAG:-latest}
build: build:
context: . context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
depends_on:
- nosql.data
- sql.data
- identity.api
- rabbitmq
- ordering.api
- marketing.api
- catalog.api
- basket.api
mobileshoppingagg: mobileshoppingagg:
image: eshop/mobileshoppingagg:${TAG:-latest} image: eshop/mobileshoppingagg:${TAG:-latest}
build: build:
context: . context: .
dockerfile: src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile dockerfile: src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
depends_on:
- nosql.data
- sql.data
- identity.api
- rabbitmq
- ordering.api
- marketing.api
- catalog.api
- basket.api
webshoppingagg: webshoppingagg:
image: eshop/webshoppingagg:${TAG:-latest} image: eshop/webshoppingagg:${TAG:-latest}
build: build:
context: . context: .
dockerfile: src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile dockerfile: src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile
depends_on:
- nosql.data
- sql.data
- identity.api
- rabbitmq
- ordering.api
- marketing.api
- catalog.api
- basket.api
ordering.signalrhub: ordering.signalrhub:
image: eshop/ordering.signalrhub:${TAG:-latest} image: eshop/ordering.signalrhub:${TAG:-latest}
build: build:
context: . context: .
dockerfile: src/Services/Ordering/Ordering.SignalrHub/Dockerfile dockerfile: src/Services/Ordering/Ordering.SignalrHub/Dockerfile
depends_on:
- nosql.data
- sql.data
- identity.api
- rabbitmq
- ordering.api
- marketing.api
- catalog.api
- basket.api
webstatus:
image: eshop/webstatus:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebStatus/Dockerfile
webspa:
image: eshop/webspa:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebSPA/Dockerfile
depends_on:
- webshoppingagg
- webshoppingapigw
- webmarketingapigw
webmvc:
image: eshop/webmvc:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebMVC/Dockerfile
depends_on:
- webshoppingagg
- webshoppingapigw
- webmarketingapigw

View File

@ -68,10 +68,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Health
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience", "{FBF43D93-F2E7-4FF8-B4AB-186895949B88}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Resilience.Http", "src\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj", "{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}"
@ -130,7 +126,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web.Shopping.HttpAggregator
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.BackgroundTasks", "src\Services\Ordering\Ordering.BackgroundTasks\Ordering.BackgroundTasks.csproj", "{7D63ED4A-3EDA-4BBA-8BBA-F46BD6430931}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.BackgroundTasks", "src\Services\Ordering\Ordering.BackgroundTasks\Ordering.BackgroundTasks.csproj", "{7D63ED4A-3EDA-4BBA-8BBA-F46BD6430931}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ordering.SignalrHub", "src\Services\Ordering\Ordering.SignalrHub\Ordering.SignalrHub.csproj", "{E1D2B260-4E7F-4A88-BC13-9910F7C44623}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.SignalrHub", "src\Services\Ordering\Ordering.SignalrHub\Ordering.SignalrHub.csproj", "{E1D2B260-4E7F-4A88-BC13-9910F7C44623}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -866,54 +862,6 @@ Global
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|Any CPU.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|ARM.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|ARM.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhone.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhone.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -1617,8 +1565,6 @@ Global
{A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379} {942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
{022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} {022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5} {1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5}
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379} {22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}

View File

@ -67,10 +67,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Health
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebStatus", "src\Web\WebStatus\WebStatus.csproj", "{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resilience", "Resilience", "{FBF43D93-F2E7-4FF8-B4AB-186895949B88}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Resilience.Http", "src\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj", "{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{022E145D-1593-47EE-9608-8E323D3C63F5}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{1A01AF82-6FCB-464C-B39C-F127AEBD315D}"
@ -857,54 +853,6 @@ Global
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x64.Build.0 = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.ActiveCfg = Release|Any CPU
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F}.Release|x86.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.AppStore|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|ARM.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhone.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x64.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x64.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x86.ActiveCfg = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Debug|x86.Build.0 = Debug|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|Any CPU.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|ARM.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|ARM.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhone.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhone.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x64.Build.0 = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.ActiveCfg = Release|Any CPU
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502}.Release|x86.Build.0 = Release|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU {1A01AF82-6FCB-464C-B39C-F127AEBD315D}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
@ -1840,8 +1788,6 @@ Global
{A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} {A81ECBC2-6B00-4DCD-8388-469174033379} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379} {942ED6E8-0050-495F-A0EA-01E97F63760C} = {A81ECBC2-6B00-4DCD-8388-469174033379}
{C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04} {C0A7918D-B4F2-4E7F-8DE2-1E5279EF079F} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
{FBF43D93-F2E7-4FF8-B4AB-186895949B88} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{D1C47FF1-91F1-4CAF-9ABB-AD642B821502} = {FBF43D93-F2E7-4FF8-B4AB-186895949B88}
{022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} {022E145D-1593-47EE-9608-8E323D3C63F5} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5} {1A01AF82-6FCB-464C-B39C-F127AEBD315D} = {022E145D-1593-47EE-9608-8E323D3C63F5}
{22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379} {22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379}

View File

@ -1,5 +1,5 @@
{ // {
"sdk": { // "sdk": {
"version": "2.1.4" // "version": "2.1.4"
} // }
} // }

View File

@ -1,8 +1,8 @@
FROM microsoft/aspnetcore:2.0 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/
RUN dotnet restore src/ApiGateways/ApiGw-Base/ RUN dotnet restore src/ApiGateways/ApiGw-Base/

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -9,8 +9,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" /> <PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Ocelot" Version="3.0.0" /> <PackageReference Include="Ocelot" Version="3.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
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 _httpContextAccesor;
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor)
{
_httpContextAccesor = httpContextAccesor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var authorizationHeader = _httpContextAccesor.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 _httpContextAccesor.HttpContext
.GetTokenAsync(ACCESS_TOKEN);
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</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>
@ -12,16 +12,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.1.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="1.1.0" />
</ItemGroup> <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0" />
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,53 +1,41 @@
using System; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using System.Collections.Generic; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; using System.Net.Http;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
public class BasketService : IBasketService public class BasketService : IBasketService
{ {
private readonly IHttpClient _apiClient; private readonly HttpClient _httpClient;
private readonly ILogger<BasketService> _logger; private readonly ILogger<BasketService> _logger;
private readonly UrlsConfig _urls; private readonly UrlsConfig _urls;
private readonly IHttpContextAccessor _httpContextAccessor;
public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config) public BasketService(HttpClient httpClient, ILogger<BasketService> logger, IOptions<UrlsConfig> config)
{ {
_apiClient = httpClient; _httpClient = httpClient;
_logger = logger; _logger = logger;
_urls = config.Value; _urls = config.Value;
_httpContextAccessor = httpContextAccessor;
} }
public async Task<BasketData> GetById(string id) public async Task<BasketData> GetById(string id)
{ {
var token = await GetUserTokenAsync(); var data = await _httpClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id));
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token);
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null;
return basket; return basket;
} }
public async Task Update(BasketData currentBasket) public async Task Update(BasketData currentBasket)
{ {
var token = await GetUserTokenAsync(); var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json");
var data = await _apiClient.PostAsync<BasketData>(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token);
int i = 0;
}
async Task<string> GetUserTokenAsync() var data = await _httpClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent);
{
var context = _httpContextAccessor.HttpContext;
return await context.GetTokenAsync("access_token");
} }
} }
} }

View File

@ -1,43 +1,41 @@
using System; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using System.Collections.Generic; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; using System.Collections.Generic;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
public class CatalogService : ICatalogService public class CatalogService : ICatalogService
{ {
private readonly HttpClient _httpClient;
private readonly IHttpClient _apiClient;
private readonly ILogger<CatalogService> _logger; private readonly ILogger<CatalogService> _logger;
private readonly UrlsConfig _urls; private readonly UrlsConfig _urls;
public CatalogService(IHttpClient httpClient, ILogger<CatalogService> logger, IOptionsSnapshot<UrlsConfig> config) public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<UrlsConfig> config)
{ {
_apiClient = httpClient; _httpClient = httpClient;
_logger = logger; _logger = logger;
_urls = config.Value; _urls = config.Value;
} }
public async Task<CatalogItem> GetCatalogItem(int id) public async Task<CatalogItem> GetCatalogItem(int id)
{ {
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id));
var item = JsonConvert.DeserializeObject<CatalogItem>(data); var catalogItem = JsonConvert.DeserializeObject<CatalogItem>(stringContent);
return item;
return catalogItem;
} }
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids) public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids)
{ {
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids));
var item = JsonConvert.DeserializeObject<CatalogItem[]>(data); var catalogItems = JsonConvert.DeserializeObject<CatalogItem[]>(stringContent);
return item;
return catalogItems;
} }
} }
} }

View File

@ -1,24 +1,20 @@
using System; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using System.Collections.Generic; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; using System.Net.Http;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{ {
public class OrderApiClient : IOrderApiClient public class OrderApiClient : IOrderApiClient
{ {
private readonly HttpClient _apiClient;
private readonly IHttpClient _apiClient;
private readonly ILogger<OrderApiClient> _logger; private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls; private readonly UrlsConfig _urls;
public OrderApiClient(IHttpClient httpClient, ILogger<OrderApiClient> logger, IOptionsSnapshot<UrlsConfig> config) public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
{ {
_apiClient = httpClient; _apiClient = httpClient;
_logger = logger; _logger = logger;
@ -27,11 +23,15 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket) public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket)
{ {
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var response = await _apiClient.PostAsync<BasketData>(url, basket); var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(uri, content);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<OrderData>(jsonResponse); var ordersDraftResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<OrderData>(ordersDraftResponse);
} }
} }
} }

View File

@ -1,21 +1,21 @@
using System; using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.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.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Polly;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; using Polly.Extensions.Http;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{ {
@ -31,84 +31,16 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddCustomMvc(Configuration)
services.AddSingleton<IHttpClient, StandardHttpClient>(); .AddCustomAuthentication(Configuration)
services.AddTransient<ICatalogService, CatalogService>(); .AddHttpServices();
services.AddTransient<IBasketService, BasketService>();
services.AddTransient<IOrderApiClient, OrderApiClient>();
services.AddOptions();
services.Configure<UrlsConfig>(Configuration.GetSection("urls"));
services.AddMvc();
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Shopping Aggregator for Mobile Clients",
Version = "v1",
Description = "Shopping Aggregator for Mobile Clients",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{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.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
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";
options.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = async ctx =>
{
int i = 0;
},
OnTokenValidated = async ctx =>
{
int i = 0;
}
};
});
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
var pathBase = Configuration["PATH_BASE"]; var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase)) if (!string.IsNullOrEmpty(pathBase))
{ {
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
@ -131,8 +63,122 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI"); c.ConfigureOAuth2("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
}); });
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
services.AddMvc();
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Shopping Aggregator for Mobile Clients",
Version = "v1",
Description = "Shopping Aggregator for Mobile Clients",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{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.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
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";
options.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = async ctx =>
{
int i = 0;
},
OnTokenValidated = async ctx =>
{
int i = 0;
}
};
});
return services;
}
public static IServiceCollection AddHttpServices(this IServiceCollection services)
{
//register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//register http services
services.AddHttpClient<IBasketService, BasketService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ICatalogService, CatalogService>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
return services;
}
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
} }
} }
} }

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/ApiGateways/Web.Bff.Shopping/aggregator WORKDIR /src/src/ApiGateways/Web.Bff.Shopping/aggregator
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure
{
public class HttpClientAuthorizationDelegatingHandler
: DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccesor;
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor)
{
_httpContextAccesor = httpContextAccesor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var authorizationHeader = _httpContextAccesor.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 _httpContextAccesor.HttpContext
.GetTokenAsync(ACCESS_TOKEN);
}
}
}

View File

@ -1,53 +1,39 @@
using System; using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using System.Collections.Generic; using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; using System.Net.Http;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{ {
public class BasketService : IBasketService public class BasketService : IBasketService
{ {
private readonly IHttpClient _apiClient; private readonly HttpClient _apiClient;
private readonly ILogger<BasketService> _logger; private readonly ILogger<BasketService> _logger;
private readonly UrlsConfig _urls; private readonly UrlsConfig _urls;
private readonly IHttpContextAccessor _httpContextAccessor;
public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config) public BasketService(HttpClient httpClient,ILogger<BasketService> logger, IOptions<UrlsConfig> config)
{ {
_apiClient = httpClient; _apiClient = httpClient;
_logger = logger; _logger = logger;
_urls = config.Value; _urls = config.Value;
_httpContextAccessor = httpContextAccessor;
} }
public async Task<BasketData> GetById(string id) public async Task<BasketData> GetById(string id)
{ {
var token = await GetUserTokenAsync(); var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id));
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token);
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null;
return basket; return basket;
} }
public async Task Update(BasketData currentBasket) public async Task Update(BasketData currentBasket)
{ {
var token = await GetUserTokenAsync(); var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json");
var data = await _apiClient.PostAsync<BasketData>(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token);
int i = 0;
}
async Task<string> GetUserTokenAsync() var data = await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent);
{
var context = _httpContextAccessor.HttpContext;
return await context.GetTokenAsync("access_token");
} }
} }
} }

View File

@ -1,43 +1,42 @@
using System; using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using System.Collections.Generic; using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; using System.Collections.Generic;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{ {
public class CatalogService : ICatalogService public class CatalogService : ICatalogService
{ {
private readonly IHttpClient _apiClient; private readonly HttpClient _httpClient;
private readonly ILogger<CatalogService> _logger; private readonly ILogger<CatalogService> _logger;
private readonly UrlsConfig _urls; private readonly UrlsConfig _urls;
public CatalogService(IHttpClient httpClient, ILogger<CatalogService> logger, IOptionsSnapshot<UrlsConfig> config) public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<UrlsConfig> config)
{ {
_apiClient = httpClient; _httpClient = httpClient;
_logger = logger; _logger = logger;
_urls = config.Value; _urls = config.Value;
} }
public async Task<CatalogItem> GetCatalogItem(int id) public async Task<CatalogItem> GetCatalogItem(int id)
{ {
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id));
var item = JsonConvert.DeserializeObject<CatalogItem>(data); var catalogItem = JsonConvert.DeserializeObject<CatalogItem>(stringContent);
return item;
return catalogItem;
} }
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids) public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids)
{ {
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids));
var item = JsonConvert.DeserializeObject<CatalogItem[]>(data); var catalogItems = JsonConvert.DeserializeObject<CatalogItem[]>(stringContent);
return item;
return catalogItems;
} }
} }
} }

View File

@ -1,24 +1,21 @@
using System; using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using System.Collections.Generic; using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; using System.Net.Http;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{ {
public class OrderApiClient : IOrderApiClient public class OrderApiClient : IOrderApiClient
{ {
private readonly IHttpClient _apiClient; private readonly HttpClient _apiClient;
private readonly ILogger<OrderApiClient> _logger; private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls; private readonly UrlsConfig _urls;
public OrderApiClient(IHttpClient httpClient, ILogger<OrderApiClient> logger, IOptionsSnapshot<UrlsConfig> config) public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
{ {
_apiClient = httpClient; _apiClient = httpClient;
_logger = logger; _logger = logger;
@ -28,10 +25,14 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket) public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket)
{ {
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var response = await _apiClient.PostAsync<BasketData>(url, basket); var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(url, content);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<OrderData>(jsonResponse); var ordersDraftResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<OrderData>(ordersDraftResponse);
} }
} }
} }

View File

@ -1,21 +1,22 @@
using System; using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Polly;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; using Polly.Extensions.Http;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; using Polly.Timeout;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
{ {
@ -31,55 +32,48 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddCustomMvc(Configuration)
services.AddSingleton<IHttpClient, StandardHttpClient>(); .AddCustomAuthentication(Configuration)
services.AddTransient<ICatalogService, CatalogService>(); .AddApplicationServices();
services.AddTransient<IBasketService, BasketService>(); }
services.AddTransient<IOrderApiClient, OrderApiClient>();
services.AddOptions(); // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
services.Configure<UrlsConfig>(Configuration.GetSection("urls")); public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
services.AddMvc(); var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
services.AddSwaggerGen(options =>
{ {
options.DescribeAllEnumsAsStrings(); loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info app.UsePathBase(pathBase);
{ }
Title = "Shopping Aggregator for Web Clients",
Version = "v1",
Description = "Shopping Aggregator for Web Clients",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme app.UseCors("CorsPolicy");
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "webshoppingagg", "Shopping Aggregator for Web Clients" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>(); if (env.IsDevelopment())
});
services.AddCors(options =>
{ {
options.AddPolicy("CorsPolicy", app.UseDeveloperExceptionPage();
builder => builder.AllowAnyOrigin() }
.AllowAnyMethod()
.AllowAnyHeader() app.UseAuthentication();
.AllowCredentials());
}); app.UseMvc();
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
//c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
});
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var identityUrl = Configuration.GetValue<string>("urls:identity"); var identityUrl = configuration.GetValue<string>("urls:identity");
services.AddAuthentication(options => services.AddAuthentication(options =>
{ {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
@ -102,37 +96,92 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
} }
}; };
}); });
return services;
}
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
services.AddMvc();
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Shopping Aggregator for Web Clients",
Version = "v1",
Description = "Shopping Aggregator for Web Clients",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "webshoppingagg", "Shopping Aggregator for Web Clients" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
//register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//register http services
services.AddHttpClient<IBasketService, BasketService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ICatalogService, CatalogService>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
return services;
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
var pathBase = Configuration["PATH_BASE"]; }
if (!string.IsNullOrEmpty(pathBase)) static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{ {
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); return HttpPolicyExtensions
app.UsePathBase(pathBase); .HandleTransientHttpError()
} .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
});
} }
} }
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>Web.Shopping.HttpAggregator</AssemblyName> <AssemblyName>Web.Shopping.HttpAggregator</AssemblyName>
<RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace> <RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
@ -12,16 +12,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.1.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
</ItemGroup> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="2.4.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0" />
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -7,12 +7,12 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac" Version="4.6.2" /> <PackageReference Include="Autofac" Version="4.6.2" />
<PackageReference Include="Microsoft.CSharp" Version="4.4.1" /> <PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Polly" Version="5.8.0" /> <PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="RabbitMQ.Client" Version="5.0.1" /> <PackageReference Include="RabbitMQ.Client" Version="5.0.1" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -7,9 +7,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac" Version="4.6.2" /> <PackageReference Include="Autofac" Version="4.6.2" />
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="2.0.0" /> <PackageReference Include="Microsoft.Azure.ServiceBus" Version="3.0.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.4.1" /> <PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -6,18 +6,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" /> </ItemGroup>
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\EventBus\EventBus.csproj" /> <ProjectReference Include="..\EventBus\EventBus.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -13,7 +13,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.1" /> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -16,10 +16,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.1" /> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> <PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" /> <PackageReference Include="System.Threading.Thread" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.4.0" /> <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" />
<PackageReference Include="WindowsAzure.Storage" Version="9.1.0" /> <PackageReference Include="WindowsAzure.Storage" Version="9.1.0" />
</ItemGroup> </ItemGroup>

View File

@ -9,7 +9,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.4.2" /> <PackageReference Include="System.Data.SqlClient" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -9,12 +9,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> <PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.Threading.Thread" Version="4.3.0" /> <PackageReference Include="System.Threading.Thread" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.4.0" /> <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,16 +0,0 @@
using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
{
public interface IHttpClient
{
Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
}
}

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
<PackageReference Include="Polly" Version="5.8.0" />
</ItemGroup>
</Project>

View File

@ -1,191 +0,0 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Polly;
using Polly.Wrap;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
{
/// <summary>
/// HttpClient wrapper that integrates Retry and Circuit
/// breaker policies when invoking HTTP services.
/// Based on Polly library: https://github.com/App-vNext/Polly
/// </summary>
public class ResilientHttpClient : IHttpClient
{
private readonly HttpClient _client;
private readonly ILogger<ResilientHttpClient> _logger;
private readonly Func<string, IEnumerable<Policy>> _policyCreator;
private ConcurrentDictionary<string, PolicyWrap> _policyWrappers;
private readonly IHttpContextAccessor _httpContextAccessor;
public ResilientHttpClient(Func<string, IEnumerable<Policy>> policyCreator, ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor)
{
_client = new HttpClient();
_logger = logger;
_policyCreator = policyCreator;
_policyWrappers = new ConcurrentDictionary<string, PolicyWrap>();
_httpContextAccessor = httpContextAccessor;
}
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return DoPostPutAsync(HttpMethod.Post, uri, item, authorizationToken, requestId, authorizationMethod);
}
public Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return DoPostPutAsync(HttpMethod.Put, uri, item, authorizationToken, requestId, authorizationMethod);
}
public Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
var origin = GetOriginFromUri(uri);
return HttpInvoker(origin, async () =>
{
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri);
SetAuthorizationHeader(requestMessage);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
}
return await _client.SendAsync(requestMessage);
});
}
public Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
{
var origin = GetOriginFromUri(uri);
return HttpInvoker(origin, async () =>
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
SetAuthorizationHeader(requestMessage);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
var response = await _client.SendAsync(requestMessage);
// raise exception if HttpResponseCode 500
// needed for circuit breaker to track fails
if (response.StatusCode == HttpStatusCode.InternalServerError)
{
throw new HttpRequestException();
}
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadAsStringAsync();
});
}
private Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
if (method != HttpMethod.Post && method != HttpMethod.Put)
{
throw new ArgumentException("Value must be either post or put.", nameof(method));
}
// a new StringContent must be created for each retry
// as it is disposed after each call
var origin = GetOriginFromUri(uri);
return HttpInvoker(origin, async () =>
{
var requestMessage = new HttpRequestMessage(method, uri);
SetAuthorizationHeader(requestMessage);
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
}
var response = await _client.SendAsync(requestMessage);
// raise exception if HttpResponseCode 500
// needed for circuit breaker to track fails
if (response.StatusCode == HttpStatusCode.InternalServerError)
{
throw new HttpRequestException();
}
return response;
});
}
private async Task<T> HttpInvoker<T>(string origin, Func<Task<T>> action)
{
var normalizedOrigin = NormalizeOrigin(origin);
if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap))
{
policyWrap = Policy.WrapAsync(_policyCreator(normalizedOrigin).ToArray());
_policyWrappers.TryAdd(normalizedOrigin, policyWrap);
}
// Executes the action applying all
// the policies defined in the wrapper
return await policyWrap.ExecuteAsync(action, new Context(normalizedOrigin));
}
private static string NormalizeOrigin(string origin)
{
return origin?.Trim()?.ToLower();
}
private static string GetOriginFromUri(string uri)
{
var url = new Uri(uri);
var origin = $"{url.Scheme}://{url.DnsSafeHost}:{url.Port}";
return origin;
}
private void SetAuthorizationHeader(HttpRequestMessage requestMessage)
{
var authorizationHeader = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authorizationHeader))
{
requestMessage.Headers.Add("Authorization", new List<string>() { authorizationHeader });
}
}
}
}

View File

@ -1,125 +0,0 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
{
public class StandardHttpClient : IHttpClient
{
private HttpClient _client;
private ILogger<StandardHttpClient> _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
public StandardHttpClient(ILogger<StandardHttpClient> logger, IHttpContextAccessor httpContextAccessor)
{
_client = new HttpClient();
_logger = logger;
_httpContextAccessor = httpContextAccessor;
}
public async Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
SetAuthorizationHeader(requestMessage);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
var response = await _client.SendAsync(requestMessage);
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadAsStringAsync();
}
private async Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
if (method != HttpMethod.Post && method != HttpMethod.Put)
{
throw new ArgumentException("Value must be either post or put.", nameof(method));
}
// a new StringContent must be created for each retry
// as it is disposed after each call
var requestMessage = new HttpRequestMessage(method, uri);
SetAuthorizationHeader(requestMessage);
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json");
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
}
var response = await _client.SendAsync(requestMessage);
// raise exception if HttpResponseCode 500
// needed for circuit breaker to track fails
if (response.StatusCode == HttpStatusCode.InternalServerError)
{
throw new HttpRequestException();
}
return response;
}
public async Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return await DoPostPutAsync(HttpMethod.Post, uri, item, authorizationToken, requestId, authorizationMethod);
}
public async Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
return await DoPostPutAsync(HttpMethod.Put, uri, item, authorizationToken, requestId, authorizationMethod);
}
public async Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri);
SetAuthorizationHeader(requestMessage);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
}
return await _client.SendAsync(requestMessage);
}
private void SetAuthorizationHeader(HttpRequestMessage requestMessage)
{
var authorizationHeader = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authorizationHeader))
{
requestMessage.Headers.Add("Authorization", new List<string>() { authorizationHeader });
}
}
}
}

View File

@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Polly" Version="5.8.0" /> <PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -27,9 +27,9 @@ namespace Microsoft.AspNetCore.Hosting
var retry = Policy.Handle<SqlException>() var retry = Policy.Handle<SqlException>()
.WaitAndRetry(new TimeSpan[] .WaitAndRetry(new TimeSpan[]
{ {
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(8),
TimeSpan.FromSeconds(15),
}); });
retry.Execute(() => retry.Execute(() =>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
@ -14,12 +14,14 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -36,7 +36,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
var basket = await _repository.GetBasketAsync(id); var basket = await _repository.GetBasketAsync(id);
if (basket == null) if (basket == null)
{ {
return NotFound(); return Ok(new CustomerBasket(id) { });
} }
return Ok(basket); return Ok(basket);

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Basket/Basket.API WORKDIR /src/src/Services/Basket/Basket.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Catalog.API</AssemblyName> <AssemblyName>Catalog.API</AssemblyName>
@ -36,18 +36,15 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" /> <PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" />

View File

@ -15,6 +15,7 @@ using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
{ {
[Route("api/v1/[controller]")] [Route("api/v1/[controller]")]
[ApiController]
public class CatalogController : ControllerBase public class CatalogController : ControllerBase
{ {
private readonly CatalogContext _catalogContext; private readonly CatalogContext _catalogContext;
@ -25,8 +26,8 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
{ {
_catalogContext = context ?? throw new ArgumentNullException(nameof(context)); _catalogContext = context ?? throw new ArgumentNullException(nameof(context));
_catalogIntegrationEventService = catalogIntegrationEventService ?? throw new ArgumentNullException(nameof(catalogIntegrationEventService)); _catalogIntegrationEventService = catalogIntegrationEventService ?? throw new ArgumentNullException(nameof(catalogIntegrationEventService));
_settings = settings.Value; _settings = settings.Value;
((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; ((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
} }

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 // For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
{ {
public class PicController : Controller public class PicController : Controller
{ {
private readonly IHostingEnvironment _env; private readonly IHostingEnvironment _env;

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Catalog/Catalog.API WORKDIR /src/src/Services/Catalog/Catalog.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,6 +1,7 @@
using Catalog.API.Infrastructure.ActionResults; using Catalog.API.Infrastructure.ActionResults;
using Catalog.API.Infrastructure.Exceptions; using Catalog.API.Infrastructure.Exceptions;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -27,12 +28,16 @@ namespace Catalog.API.Infrastructure.Filters
if (context.Exception.GetType() == typeof(CatalogDomainException)) if (context.Exception.GetType() == typeof(CatalogDomainException))
{ {
var json = new JsonErrorResponse var problemDetails = new ValidationProblemDetails()
{ {
Messages = new[] { context.Exception.Message } Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
}; };
context.Result = new BadRequestObjectResult(json); problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() });
context.Result = new BadRequestObjectResult(problemDetails);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
} }
else else

View File

@ -1,35 +1,37 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API using Autofac;
{ using Autofac.Extensions.DependencyInjection;
using Autofac; using global::Catalog.API.Infrastructure.Filters;
using Autofac.Extensions.DependencyInjection; using global::Catalog.API.IntegrationEvents;
using global::Catalog.API.Infrastructure.Filters; using Microsoft.ApplicationInsights.Extensibility;
using global::Catalog.API.IntegrationEvents; using Microsoft.ApplicationInsights.ServiceFabric;
using Microsoft.ApplicationInsights.Extensibility; using Microsoft.AspNetCore.Builder;
using Microsoft.ApplicationInsights.ServiceFabric; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.ServiceBus; using Microsoft.Azure.ServiceBus;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.HealthChecks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using RabbitMQ.Client; using RabbitMQ.Client;
using System; using System;
using System.Data.Common; using System.Data.Common;
using System.Reflection; using System.Reflection;
namespace Microsoft.eShopOnContainers.Services.Catalog.API
{
public class Startup public class Startup
{ {
public Startup(IConfiguration configuration) public Startup(IConfiguration configuration)
@ -41,21 +43,95 @@
public IServiceProvider ConfigureServices(IServiceCollection services) public IServiceProvider ConfigureServices(IServiceCollection services)
{ {
// Add framework services. services.AddAppInsight(Configuration)
.AddCustomMVC(Configuration)
.AddCustomDbContext(Configuration)
.AddCustomOptions(Configuration)
.AddIntegrationServices(Configuration)
.AddEventBus(Configuration)
.AddSwagger();
RegisterAppInsights(services); var container = new ContainerBuilder();
container.Populate(services);
return new AutofacServiceProvider(container.Build());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//Configure logs
loggerFactory.AddAzureWebAppDiagnostics();
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
app.UsePathBase(pathBase);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200));
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
app.UseCors("CorsPolicy");
app.UseMvcWithDefaultRoute();
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Catalog.API V1");
});
ConfigureEventBus(app);
}
protected virtual void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>();
}
}
public static class CustomExtensionMethods
{
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
{
services.AddApplicationInsightsTelemetry(configuration);
var orchestratorType = configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
return services;
}
public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
var minutes = 1; var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed))
{ {
minutes = minutesParsed; minutes = minutesParsed;
} }
checks.AddSqlCheck("CatalogDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); checks.AddSqlCheck("CatalogDb", configuration["ConnectionString"], TimeSpan.FromMinutes(minutes));
var accountName = Configuration.GetValue<string>("AzureStorageAccountName"); var accountName = configuration.GetValue<string>("AzureStorageAccountName");
var accountKey = Configuration.GetValue<string>("AzureStorageAccountKey"); var accountKey = configuration.GetValue<string>("AzureStorageAccountKey");
if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey))
{ {
checks.AddAzureBlobStorageCheck(accountName, accountKey); checks.AddAzureBlobStorageCheck(accountName, accountKey);
@ -67,9 +143,23 @@
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); options.Filters.Add(typeof(HttpGlobalExceptionFilter));
}).AddControllersAsServices(); }).AddControllersAsServices();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<CatalogContext>(options => services.AddDbContext<CatalogContext>(options =>
{ {
options.UseSqlServer(Configuration["ConnectionString"], options.UseSqlServer(configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions => sqlServerOptionsAction: sqlOptions =>
{ {
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
@ -85,7 +175,7 @@
services.AddDbContext<IntegrationEventLogContext>(options => services.AddDbContext<IntegrationEventLogContext>(options =>
{ {
options.UseSqlServer(Configuration["ConnectionString"], options.UseSqlServer(configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions => sqlServerOptionsAction: sqlOptions =>
{ {
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
@ -94,9 +184,35 @@
}); });
}); });
services.Configure<CatalogSettings>(Configuration); return services;
}
// Add framework services. public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<CatalogSettings>(configuration);
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new ValidationProblemDetails(context.ModelState)
{
Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json", "application/problem+xml" }
};
};
});
return services;
}
public static IServiceCollection AddSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(options => services.AddSwaggerGen(options =>
{ {
options.DescribeAllEnumsAsStrings(); options.DescribeAllEnumsAsStrings();
@ -109,21 +225,18 @@
}); });
}); });
services.AddCors(options => return services;
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
}
public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c)); sp => (DbConnection c) => new IntegrationEventLogService(c));
services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>(); services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>();
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{ {
services.AddSingleton<IServiceBusPersisterConnection>(sp => services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{ {
@ -144,93 +257,37 @@
var factory = new ConnectionFactory() var factory = new ConnectionFactory()
{ {
HostName = Configuration["EventBusConnection"] HostName = configuration["EventBusConnection"]
}; };
if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{ {
factory.UserName = Configuration["EventBusUserName"]; factory.UserName = configuration["EventBusUserName"];
} }
if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{ {
factory.Password = Configuration["EventBusPassword"]; factory.Password = configuration["EventBusPassword"];
} }
var retryCount = 5; var retryCount = 5;
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{ {
retryCount = int.Parse(Configuration["EventBusRetryCount"]); retryCount = int.Parse(configuration["EventBusRetryCount"]);
} }
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
}); });
} }
RegisterEventBus(services); return services;
var container = new ContainerBuilder();
container.Populate(services);
return new AutofacServiceProvider(container.Build());
} }
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{ {
//Configure logs var subscriptionClientName = configuration["SubscriptionClientName"];
loggerFactory.AddConsole(Configuration.GetSection("Logging")); if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
loggerFactory.AddDebug();
loggerFactory.AddAzureWebAppDiagnostics();
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
app.UsePathBase(pathBase);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200));
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
app.UseCors("CorsPolicy");
app.UseMvcWithDefaultRoute();
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Catalog.API V1");
});
ConfigureEventBus(app);
}
private void RegisterAppInsights(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
var orchestratorType = Configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
}
private void RegisterEventBus(IServiceCollection services)
{
var subscriptionClientName = Configuration["SubscriptionClientName"];
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
{ {
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{ {
@ -254,9 +311,9 @@
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5; var retryCount = 5;
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{ {
retryCount = int.Parse(Configuration["EventBusRetryCount"]); retryCount = int.Parse(configuration["EventBusRetryCount"]);
} }
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
@ -266,12 +323,8 @@
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddTransient<OrderStatusChangedToAwaitingValidationIntegrationEventHandler>(); services.AddTransient<OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
services.AddTransient<OrderStatusChangedToPaidIntegrationEventHandler>(); services.AddTransient<OrderStatusChangedToPaidIntegrationEventHandler>();
}
protected virtual void ConfigureEventBus(IApplicationBuilder app) return services;
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>();
} }
} }
} }

View File

@ -1,12 +1,26 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS sdk-with-node
ENV NODE_VERSION 8.11.1
ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60
RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \
&& echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \
&& tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \
&& rm nodejs.tar.gz \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
FROM sdk-with-node AS updated-npm
RUN npm i -g npm
FROM updated-npm as build
RUN npm install -g bower@1.8.4
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Identity/Identity.API WORKDIR /src/src/Services/Identity/Identity.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<UserSecretsId>aspnet-eShopOnContainers.Identity-90487118-103c-4ff0-b9da-e5e26f7ab0c5</UserSecretsId> <UserSecretsId>aspnet-eShopOnContainers.Identity-90487118-103c-4ff0-b9da-e5e26f7ab0c5</UserSecretsId>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
@ -16,14 +15,15 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.1.0" /> <PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.1.0" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.1.1" /> <PackageReference Include="IdentityServer4.EntityFramework" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.1" /> <PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
</ItemGroup> </ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish"> <Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
@ -32,10 +32,7 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.5.357" /> <DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.7.385" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Location/Locations.API WORKDIR /src/src/Services/Location/Locations.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,26 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<UserSecretsId>aspnet-Locations.API-20161122013619</UserSecretsId> <UserSecretsId>aspnet-Locations.API-20161122013619</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="mongocsharpdriver" Version="2.5.0" /> <PackageReference Include="mongocsharpdriver" Version="2.5.0" />
<PackageReference Include="MongoDB.Bson" Version="2.5.0" /> <PackageReference Include="MongoDB.Bson" Version="2.5.0" />
<PackageReference Include="MongoDB.Driver" Version="2.5.0" /> <PackageReference Include="MongoDB.Driver" Version="2.5.0" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.5.0" /> <PackageReference Include="MongoDB.Driver.Core" Version="2.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="2.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="2.4.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Marketing/Marketing.API WORKDIR /src/src/Services/Marketing/Marketing.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<RootNamespace>Microsoft.eShopOnContainers.Services.Marketing.API</RootNamespace> <RootNamespace>Microsoft.eShopOnContainers.Services.Marketing.API</RootNamespace>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
@ -22,18 +22,16 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="mongocsharpdriver" Version="2.5.0" /> <PackageReference Include="mongocsharpdriver" Version="2.5.0" />
<PackageReference Include="MongoDB.Bson" Version="2.5.0" /> <PackageReference Include="MongoDB.Bson" Version="2.5.0" />
<PackageReference Include="MongoDB.Driver" Version="2.5.0" /> <PackageReference Include="MongoDB.Driver" Version="2.5.0" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.5.0" /> <PackageReference Include="MongoDB.Driver.Core" Version="2.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />

View File

@ -28,6 +28,7 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderCancelled
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
_orderingIntegrationEventService = orderingIntegrationEventService;
} }
public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken)

View File

@ -1,5 +1,6 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries
{ {
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -7,7 +8,7 @@
{ {
Task<Order> GetOrderAsync(int id); Task<Order> GetOrderAsync(int id);
Task<IEnumerable<OrderSummary>> GetOrdersAsync(); Task<IEnumerable<OrderSummary>> GetOrdersFromUserAsync(Guid userId);
Task<IEnumerable<CardType>> GetCardTypesAsync(); Task<IEnumerable<CardType>> GetCardTypesAsync();
} }

View File

@ -7,7 +7,7 @@
using System.Collections.Generic; using System.Collections.Generic;
public class OrderQueries public class OrderQueries
:IOrderQueries : IOrderQueries
{ {
private string _connectionString = string.Empty; private string _connectionString = string.Empty;
@ -42,18 +42,20 @@
} }
} }
public async Task<IEnumerable<OrderSummary>> GetOrdersAsync() public async Task<IEnumerable<OrderSummary>> GetOrdersFromUserAsync(Guid userId)
{ {
using (var connection = new SqlConnection(_connectionString)) using (var connection = new SqlConnection(_connectionString))
{ {
connection.Open(); connection.Open();
return await connection.QueryAsync<OrderSummary>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status],SUM(oi.units*oi.unitprice) as total return await connection.QueryAsync<OrderSummary>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status], SUM(oi.units*oi.unitprice) as total
FROM [ordering].[Orders] o FROM [ordering].[Orders] o
LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid
LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id
LEFT JOIN[ordering].[buyers] ob on o.BuyerId = ob.Id
WHERE ob.IdentityGuid = @userId
GROUP BY o.[Id], o.[OrderDate], os.[Name] GROUP BY o.[Id], o.[OrderDate], os.[Name]
ORDER BY o.[Id]"); ORDER BY o.[Id]", new { userId });
} }
} }

View File

@ -5,7 +5,6 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
using Ordering.API.Application.Commands; using Ordering.API.Application.Commands;
using Ordering.API.Application.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
@ -15,6 +14,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
{ {
[Route("api/v1/[controller]")] [Route("api/v1/[controller]")]
[Authorize] [Authorize]
[ApiController]
public class OrdersController : Controller public class OrdersController : Controller
{ {
private readonly IMediator _mediator; private readonly IMediator _mediator;
@ -87,8 +87,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
[ProducesResponseType(typeof(IEnumerable<OrderSummary>), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(IEnumerable<OrderSummary>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetOrders() public async Task<IActionResult> GetOrders()
{ {
var orders = await _orderQueries.GetOrdersAsync(); var userid = _identityService.GetUserIdentity();
var orders = await _orderQueries.GetOrdersFromUserAsync(Guid.Parse(userid));
return Ok(orders); return Ok(orders);
} }

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Ordering/Ordering.API WORKDIR /src/src/Services/Ordering/Ordering.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -3,6 +3,7 @@
using AspNetCore.Mvc; using AspNetCore.Mvc;
using global::Ordering.Domain.Exceptions; using global::Ordering.Domain.Exceptions;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -25,16 +26,18 @@
context.Exception, context.Exception,
context.Exception.Message); context.Exception.Message);
if (context.Exception.GetType() == typeof(OrderingDomainException)) if (context.Exception.GetType() == typeof(OrderingDomainException))
{ {
var json = new JsonErrorResponse var problemDetails = new ValidationProblemDetails()
{ {
Messages = new[] { context.Exception.Message } Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
}; };
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() });
//It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information
context.Result = new BadRequestObjectResult(json); context.Result = new BadRequestObjectResult(problemDetails);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
} }
else else
@ -52,7 +55,7 @@
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1
// It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information
context.Result = new InternalServerErrorObjectResult(json); context.Result = new InternalServerErrorObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
} }
context.ExceptionHandled = true; context.ExceptionHandled = true;
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<UserSecretsId>aspnet-Ordering.API-20161122013547</UserSecretsId> <UserSecretsId>aspnet-Ordering.API-20161122013547</UserSecretsId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
@ -31,25 +31,21 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentValidation.AspNetCore" Version="7.5.0" /> <PackageReference Include="FluentValidation.AspNetCore" Version="7.5.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="4.0.0" /> <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="4.1.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="MediatR" Version="4.0.1" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> <PackageReference Include="MediatR" Version="4.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" />
<PackageReference Include="System.Reflection" Version="4.3.0" /> <PackageReference Include="System.Reflection" Version="4.3.0" />
<PackageReference Include="Dapper" Version="1.50.4" /> <PackageReference Include="Dapper" Version="1.50.4" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Polly" Version="5.8.0" /> <PackageReference Include="Polly" Version="6.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Setup\*"> <None Update="Setup\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@ -7,8 +7,6 @@
public string EventBusConnection { get; set; } public string EventBusConnection { get; set; }
public int GracePeriodTime { get; set; }
public int CheckUpdateTime { get; set; } public int CheckUpdateTime { get; set; }
} }
} }

View File

@ -15,6 +15,7 @@
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.ServiceBus; using Microsoft.Azure.ServiceBus;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
@ -47,144 +48,15 @@
public IServiceProvider ConfigureServices(IServiceCollection services) public IServiceProvider ConfigureServices(IServiceCollection services)
{ {
RegisterAppInsights(services); services.AddApplicationInsights(Configuration)
.AddCustomMvc()
// Add framework services. .AddHealthChecks(Configuration)
services.AddMvc(options => .AddCustomDbContext(Configuration)
{ .AddCustomSwagger(Configuration)
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); .AddCustomIntegrations(Configuration)
}).AddControllersAsServices(); //Injecting Controllers themselves thru DI .AddCustomConfiguration(Configuration)
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services .AddEventBus(Configuration)
.AddCustomAuthentication(Configuration);
services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
services.AddHealthChecks(checks =>
{
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddSqlCheck("OrderingDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes));
});
services.AddEntityFrameworkSqlServer()
.AddDbContext<OrderingContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
},
ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
);
services.AddDbContext<IntegrationEventLogContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
});
services.Configure<OrderingSettings>(Configuration);
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Ordering HTTP API",
Version = "v1",
Description = "The Ordering Service HTTP API",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "orders", "Ordering API" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
// Add application services.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>();
var serviceBusConnectionString = Configuration["EventBusConnection"];
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString);
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger);
});
}
else
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = Configuration["EventBusConnection"]
};
if (!string.IsNullOrEmpty(Configuration["EventBusUserName"]))
{
factory.UserName = Configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(Configuration["EventBusPassword"]))
{
factory.Password = Configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
RegisterEventBus(services);
ConfigureAuthService(services);
services.AddOptions();
//configure autofac //configure autofac
@ -200,8 +72,6 @@
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{ {
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddAzureWebAppDiagnostics(); loggerFactory.AddAzureWebAppDiagnostics();
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
@ -219,7 +89,7 @@
app.UseCors("CorsPolicy"); app.UseCors("CorsPolicy");
ConfigureAuth(app); ConfigureAuth(app);
app.UseMvcWithDefaultRoute(); app.UseMvcWithDefaultRoute();
app.UseSwagger() app.UseSwagger()
@ -233,23 +103,6 @@
ConfigureEventBus(app); ConfigureEventBus(app);
} }
private void RegisterAppInsights(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(Configuration);
var orchestratorType = Configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
}
private void ConfigureEventBus(IApplicationBuilder app) private void ConfigureEventBus(IApplicationBuilder app)
{ {
@ -263,25 +116,6 @@
eventBus.Subscribe<OrderPaymentSuccededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>>(); eventBus.Subscribe<OrderPaymentSuccededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>>();
} }
private void ConfigureAuthService(IServiceCollection services)
{
// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "orders";
});
}
protected virtual void ConfigureAuth(IApplicationBuilder app) protected virtual void ConfigureAuth(IApplicationBuilder app)
{ {
@ -292,19 +126,219 @@
app.UseAuthentication(); app.UseAuthentication();
} }
}
private void RegisterEventBus(IServiceCollection services) static class CustomExtensionsMethods
{
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration)
{ {
var subscriptionClientName = Configuration["SubscriptionClientName"]; services.AddApplicationInsightsTelemetry(configuration);
var orchestratorType = configuration.GetValue<string>("OrchestratorType");
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
return services;
}
public static IServiceCollection AddCustomMvc(this IServiceCollection services)
{
// Add framework services.
services.AddMvc(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
}).AddControllersAsServices(); //Injecting Controllers themselves thru DI
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks(checks =>
{
var minutes = 1;
if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddSqlCheck("OrderingDb", configuration["ConnectionString"], TimeSpan.FromMinutes(minutes));
});
return services;
}
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddEntityFrameworkSqlServer()
.AddDbContext<OrderingContext>(options =>
{
options.UseSqlServer(configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
},
ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
);
services.AddDbContext<IntegrationEventLogContext>(options =>
{
options.UseSqlServer(configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
});
return services;
}
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration)
{
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Ordering HTTP API",
Version = "v1",
Description = "The Ordering Service HTTP API",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "orders", "Ordering API" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
return services;
}
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>();
var serviceBusConnectionString = configuration["EventBusConnection"];
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString);
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger);
});
}
else
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = configuration["EventBusConnection"]
};
if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{
factory.UserName = configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{
factory.Password = configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
return services;
}
public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<OrderingSettings>(configuration);
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new ValidationProblemDetails(context.ModelState)
{
Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json", "application/problem+xml" }
};
};
});
return services;
}
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
var subscriptionClientName = configuration["SubscriptionClientName"];
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{ {
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{ {
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
return new EventBusServiceBus(serviceBusPersisterConnection, logger, return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
@ -320,9 +354,9 @@
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5; var retryCount = 5;
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{ {
retryCount = int.Parse(Configuration["EventBusRetryCount"]); retryCount = int.Parse(configuration["EventBusRetryCount"]);
} }
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
@ -330,6 +364,30 @@
} }
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
var identityUrl = configuration.GetValue<string>("IdentityUrl");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "orders";
});
return services;
} }
} }
} }

View File

@ -12,7 +12,6 @@
}, },
"AzureServiceBusEnabled": false, "AzureServiceBusEnabled": false,
"SubscriptionClientName": "Ordering", "SubscriptionClientName": "Ordering",
"GracePeriodTime": "1",
"CheckUpdateTime": "30000", "CheckUpdateTime": "30000",
"ApplicationInsights": { "ApplicationInsights": {
"InstrumentationKey": "" "InstrumentationKey": ""

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Ordering/Ordering.BackgroundTasks WORKDIR /src/src/Services/Ordering/Ordering.BackgroundTasks
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,23 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Folder Include="wwwroot\" /> <Compile Remove="wwwroot\**" />
<Content Remove="wwwroot\**" />
<EmbeddedResource Remove="wwwroot\**" />
<None Remove="wwwroot\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Dapper" Version="1.50.4" /> <PackageReference Include="Dapper" Version="1.50.4" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -110,7 +110,7 @@ namespace Ordering.BackgroundTasks
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env) public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env)
{ {
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously

View File

@ -9,9 +9,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,18 +1,16 @@
FROM microsoft/aspnetcore:2.0 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY eShopOnContainers-ServicesAndWebApps.sln ./
COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/
RUN dotnet restore -nowarn:msb3202,nu1503
COPY . . COPY . .
WORKDIR /src/src/Services/Ordering/Ordering.SignalrHub WORKDIR /src/src/Services/Ordering/Ordering.SignalrHub
RUN dotnet build Ordering.SignalrHub.csproj -c Release -o /app RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore Ordering.SignalrHub.csproj -c Release -o /app
FROM build AS publish FROM build AS publish
RUN dotnet publish Ordering.SignalrHub.csproj -c Release -o /app RUN dotnet publish --no-restore Ordering.SignalrHub.csproj -c Release -o /app
FROM base AS final FROM base AS final
WORKDIR /app WORKDIR /app

View File

@ -13,13 +13,13 @@ namespace Ordering.SignalrHub
public override async Task OnConnectedAsync() public override async Task OnConnectedAsync()
{ {
await Groups.AddAsync(Context.ConnectionId, Context.User.Identity.Name); await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
await base.OnConnectedAsync(); await base.OnConnectedAsync();
} }
public override async Task OnDisconnectedAsync(Exception ex) public override async Task OnDisconnectedAsync(Exception ex)
{ {
await Groups.RemoveAsync(Context.ConnectionId, Context.User.Identity.Name); await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name);
await base.OnDisconnectedAsync(ex); await base.OnDisconnectedAsync(ex);
} }
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
@ -11,10 +11,15 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-preview2-final" /> <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.0.0-preview2-final" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Redis" Version="1.0.0-preview2-final" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Redis" Version="1.0.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -134,7 +134,7 @@ namespace Ordering.SignalrHub
app.UseSignalR(routes => app.UseSignalR(routes =>
{ {
routes.MapHub<NotificationsHub>("/notificationhub", options => routes.MapHub<NotificationsHub>("/notificationhub", options =>
options.Transports = Microsoft.AspNetCore.Http.Connections.TransportType.All); options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransports.All);
}); });
ConfigureEventBus(app); ConfigureEventBus(app);

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Services/Payment/Payment.API WORKDIR /src/src/Services/Payment/Payment.API
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
</PropertyGroup> </PropertyGroup>
@ -9,13 +9,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
</ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />

View File

@ -7,9 +7,8 @@ namespace Microsoft.eShopOnContainers.WebMVC
{ {
public class AppSettings public class AppSettings
{ {
public Connectionstrings ConnectionStrings { get; set; } //public Connectionstrings ConnectionStrings { get; set; }
public string MarketingUrl { get; set; } public string MarketingUrl { get; set; }
public string PurchaseUrl { get; set; } public string PurchaseUrl { get; set; }
public string SignalrHubUrl { get; set; } public string SignalrHubUrl { get; set; }
public bool ActivateCampaignDetailFunction { get; set; } public bool ActivateCampaignDetailFunction { get; set; }

View File

@ -71,7 +71,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{ {
var user = _appUserParser.Parse(HttpContext.User); var user = _appUserParser.Parse(HttpContext.User);
await _basketSvc.AddItemToBasket(user, productDetails.Id); await _basketSvc.AddItemToBasket(user, productDetails.Id);
//await _basketSvc.AddItemToBasket(user, product);
} }
return RedirectToAction("Index", "Catalog"); return RedirectToAction("Index", "Catalog");
} }

View File

@ -1,14 +1,9 @@
using System; using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.AspNetCore.Authorization;
using System.Net.Http;
using Polly.CircuitBreaker; using Polly.CircuitBreaker;
using WebMVC.Models; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{ {
@ -52,11 +47,12 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
} }
catch(BrokenCircuitException) catch (BrokenCircuitException)
{ {
ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on. (Business Msg Due to Circuit-Breaker)"); ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on. (Business Msg Due to Circuit-Breaker)");
} }
return View("Create", model);
return View("Create", model);
} }
public async Task<IActionResult> Cancel(string orderId) public async Task<IActionResult> Cancel(string orderId)

View File

@ -1,12 +1,9 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using System; using Newtonsoft.Json;
using System.Collections.Generic; using System.Net.Http;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace WebMVC.Controllers namespace WebMVC.Controllers
@ -14,6 +11,7 @@ namespace WebMVC.Controllers
class TestPayload class TestPayload
{ {
public int CatalogItemId { get; set; } public int CatalogItemId { get; set; }
public string BasketId { get; set; } public string BasketId { get; set; }
public int Quantity { get; set; } public int Quantity { get; set; }
@ -22,9 +20,10 @@ namespace WebMVC.Controllers
[Authorize] [Authorize]
public class TestController : Controller public class TestController : Controller
{ {
private readonly IHttpClient _client; private readonly IHttpClientFactory _client;
private readonly IIdentityParser<ApplicationUser> _appUserParser; private readonly IIdentityParser<ApplicationUser> _appUserParser;
public TestController(IHttpClient client, IIdentityParser<ApplicationUser> identityParser)
public TestController(IHttpClientFactory client, IIdentityParser<ApplicationUser> identityParser)
{ {
_client = client; _client = client;
_appUserParser = identityParser; _appUserParser = identityParser;
@ -33,18 +32,24 @@ namespace WebMVC.Controllers
public async Task<IActionResult> Ocelot() public async Task<IActionResult> Ocelot()
{ {
var url = "http://apigw/shopping/api/v1/basket/items"; var url = "http://apigw/shopping/api/v1/basket/items";
var payload = new TestPayload() var payload = new TestPayload()
{ {
CatalogItemId = 1, CatalogItemId = 1,
Quantity = 1, Quantity = 1,
BasketId = _appUserParser.Parse(User).Id BasketId = _appUserParser.Parse(User).Id
}; };
var token = await HttpContext.GetTokenAsync("access_token");
var response = await _client.PostAsync<TestPayload>(url, payload, token); var content = new StringContent(JsonConvert.SerializeObject(payload), System.Text.Encoding.UTF8, "application/json");
var response = await _client.CreateClient(nameof(IBasketService))
.PostAsync(url, content);
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
var str = await response.Content.ReadAsStringAsync(); var str = await response.Content.ReadAsStringAsync();
return Ok(str); return Ok(str);
} }
else else

View File

@ -1,12 +1,25 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS sdk-with-node
ENV NODE_VERSION 8.11.1
ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60
RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \
&& echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \
&& tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \
&& rm nodejs.tar.gz \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
FROM sdk-with-node AS updated-npm
RUN npm i -g npm
FROM updated-npm as build
RUN npm install -g bower@1.8.4
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Web/WebMVC WORKDIR /src/src/Web/WebMVC
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace WebMVC.Infrastructure
{
public class HttpClientAuthorizationDelegatingHandler
: DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccesor;
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor)
{
_httpContextAccesor = httpContextAccesor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var authorizationHeader = _httpContextAccesor.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 _httpContextAccesor.HttpContext
.GetTokenAsync(ACCESS_TOKEN);
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace WebMVC.Infrastructure
{
public class HttpClientRequestIdDelegatingHandler
: DelegatingHandler
{
public HttpClientRequestIdDelegatingHandler()
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put)
{
request.Headers.Add("x-requestid", Guid.NewGuid().ToString());
}
return await base.SendAsync(request, cancellationToken);
}
}
}

View File

@ -1,10 +0,0 @@
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using System;
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
{
public interface IResilientHttpClientFactory
{
ResilientHttpClient CreateResilientHttpClient();
}
}

View File

@ -1,66 +0,0 @@
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging;
using Polly;
using System;
using System.Net.Http;
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
{
public class ResilientHttpClientFactory : IResilientHttpClientFactory
{
private readonly ILogger<ResilientHttpClient> _logger;
private readonly int _retryCount;
private readonly int _exceptionsAllowedBeforeBreaking;
private readonly IHttpContextAccessor _httpContextAccessor;
public ResilientHttpClientFactory(ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor, int exceptionsAllowedBeforeBreaking = 5, int retryCount = 6)
{
_logger = logger;
_exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
_retryCount = retryCount;
_httpContextAccessor = httpContextAccessor;
}
public ResilientHttpClient CreateResilientHttpClient()
=> new ResilientHttpClient((origin) => CreatePolicies(), _logger, _httpContextAccessor);
private Policy[] CreatePolicies()
=> new Policy[]
{
Policy.Handle<HttpRequestException>()
.WaitAndRetryAsync(
// number of retries
_retryCount,
// exponential backofff
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
// on retry
(exception, timeSpan, retryCount, context) =>
{
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
$"of {context.PolicyKey} " +
$"at {context.ExecutionKey}, " +
$"due to: {exception}.";
_logger.LogWarning(msg);
_logger.LogDebug(msg);
}),
Policy.Handle<HttpRequestException>()
.CircuitBreakerAsync(
// number of exceptions before breaking circuit
_exceptionsAllowedBeforeBreaking,
// time circuit opened before retry
TimeSpan.FromMinutes(1),
(exception, duration) =>
{
// on circuit opened
_logger.LogTrace("Circuit breaker opened");
},
() =>
{
// on circuit closed
_logger.LogTrace("Circuit breaker reset");
})
};
}
}

View File

@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WebMVC.Infrastructure; using WebMVC.Infrastructure;
using WebMVC.Models; using WebMVC.Models;
@ -14,42 +12,40 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
{ {
public class BasketService : IBasketService public class BasketService : IBasketService
{ {
private readonly IOptionsSnapshot<AppSettings> _settings; private readonly IOptions<AppSettings> _settings;
private readonly IHttpClient _apiClient; private readonly HttpClient _apiClient;
private readonly string _basketByPassUrl; private readonly string _basketByPassUrl;
private readonly string _purchaseUrl; private readonly string _purchaseUrl;
private readonly IHttpContextAccessor _httpContextAccesor;
private readonly string _bffUrl; private readonly string _bffUrl;
public BasketService(IOptionsSnapshot<AppSettings> settings, public BasketService(HttpClient httpClient, IOptions<AppSettings> settings)
IHttpContextAccessor httpContextAccesor, IHttpClient httpClient)
{ {
_apiClient = httpClient;
_settings = settings; _settings = settings;
_basketByPassUrl = $"{_settings.Value.PurchaseUrl}/api/v1/b/basket"; _basketByPassUrl = $"{_settings.Value.PurchaseUrl}/api/v1/b/basket";
_purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1"; _purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1";
_httpContextAccesor = httpContextAccesor;
_apiClient = httpClient;
} }
public async Task<Basket> GetBasket(ApplicationUser user) public async Task<Basket> GetBasket(ApplicationUser user)
{ {
var token = await GetUserTokenAsync(); var uri = API.Basket.GetBasket(_basketByPassUrl, user.Id);
var getBasketUri = API.Basket.GetBasket(_basketByPassUrl, user.Id);
var dataString = await _apiClient.GetStringAsync(getBasketUri, token); var responseString = await _apiClient.GetStringAsync(uri);
return string.IsNullOrEmpty(dataString) ? return string.IsNullOrEmpty(responseString) ?
new Basket() { BuyerId = user.Id} : new Basket() { BuyerId = user.Id } :
JsonConvert.DeserializeObject<Basket>(dataString); JsonConvert.DeserializeObject<Basket>(responseString);
} }
public async Task<Basket> UpdateBasket(Basket basket) public async Task<Basket> UpdateBasket(Basket basket)
{ {
var token = await GetUserTokenAsync(); var uri = API.Basket.UpdateBasket(_basketByPassUrl);
var updateBasketUri = API.Basket.UpdateBasket(_basketByPassUrl);
var response = await _apiClient.PostAsync(updateBasketUri, basket, token); var basketContent = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(uri, basketContent);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
@ -58,65 +54,64 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task Checkout(BasketDTO basket) public async Task Checkout(BasketDTO basket)
{ {
var token = await GetUserTokenAsync(); var uri = API.Basket.CheckoutBasket(_basketByPassUrl);
var updateBasketUri = API.Basket.CheckoutBasket(_basketByPassUrl); var basketContent = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(updateBasketUri, basket, token); var response = await _apiClient.PostAsync(uri, basketContent);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities) public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities)
{ {
var uri = API.Purchase.UpdateBasketItem(_purchaseUrl);
var token = await GetUserTokenAsync(); var basketUpdate = new
var updateBasketUri = API.Purchase.UpdateBasketItem(_purchaseUrl);
var userId = user.Id;
var response = await _apiClient.PutAsync(updateBasketUri, new
{ {
BasketId = userId, BasketId = user.Id,
Updates = quantities.Select(kvp => new Updates = quantities.Select(kvp => new
{ {
BasketItemId = kvp.Key, BasketItemId = kvp.Key,
NewQty = kvp.Value NewQty = kvp.Value
}).ToArray() }).ToArray()
}, token); };
var basketContent = new StringContent(JsonConvert.SerializeObject(basketUpdate), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PutAsync(uri, basketContent);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync(); var jsonResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Basket>(jsonResponse); return JsonConvert.DeserializeObject<Basket>(jsonResponse);
} }
public async Task<Order> GetOrderDraft(string basketId) public async Task<Order> GetOrderDraft(string basketId)
{ {
var token = await GetUserTokenAsync(); var uri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId);
var draftOrderUri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId);
var json = await _apiClient.GetStringAsync(draftOrderUri, token); var responseString = await _apiClient.GetStringAsync(uri);
return JsonConvert.DeserializeObject<Order>(json);
var response = JsonConvert.DeserializeObject<Order>(responseString);
return response;
} }
public async Task AddItemToBasket(ApplicationUser user, int productId) public async Task AddItemToBasket(ApplicationUser user, int productId)
{ {
var token = await GetUserTokenAsync(); var uri = API.Purchase.AddItemToBasket(_purchaseUrl);
var updateBasketUri = API.Purchase.AddItemToBasket(_purchaseUrl);
var userId = user.Id;
var response = await _apiClient.PostAsync(updateBasketUri, new var newItem = new
{ {
CatalogItemId = productId, CatalogItemId = productId,
BasketId = userId, BasketId = user.Id,
Quantity = 1 Quantity = 1
}, token); };
} var basketContent = new StringContent(JsonConvert.SerializeObject(newItem), System.Text.Encoding.UTF8, "application/json");
async Task<string> GetUserTokenAsync() var response = await _apiClient.PostAsync(uri, basketContent);
{
var context = _httpContextAccesor.HttpContext;
return await context.GetTokenAsync("access_token");
} }
} }
} }

View File

@ -1,69 +1,49 @@
namespace Microsoft.eShopOnContainers.WebMVC.Services namespace Microsoft.eShopOnContainers.WebMVC.Services
{ {
using global::WebMVC.Infrastructure; using global::WebMVC.Infrastructure;
using AspNetCore.Authentication;
using AspNetCore.Http;
using BuildingBlocks.Resilience.Http;
using ViewModels;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using ViewModels;
public class CampaignService : ICampaignService public class CampaignService : ICampaignService
{ {
private readonly IOptionsSnapshot<AppSettings> _settings; private readonly IOptions<AppSettings> _settings;
private readonly IHttpClient _apiClient; private readonly HttpClient _httpClient;
private readonly ILogger<CampaignService> _logger; private readonly ILogger<CampaignService> _logger;
private readonly string _remoteServiceBaseUrl; private readonly string _remoteServiceBaseUrl;
private readonly IHttpContextAccessor _httpContextAccesor;
public CampaignService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient, public CampaignService(IOptions<AppSettings> settings, HttpClient httpClient, ILogger<CampaignService> logger)
ILogger<CampaignService> logger, IHttpContextAccessor httpContextAccesor)
{ {
_settings = settings; _settings = settings;
_apiClient = httpClient; _httpClient = httpClient;
_logger = logger; _logger = logger;
_remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/m/campaigns/"; _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/m/campaigns/";
_httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor));
} }
public async Task<Campaign> GetCampaigns(int pageSize, int pageIndex) public async Task<Campaign> GetCampaigns(int pageSize, int pageIndex)
{ {
var allCampaignItemsUri = API.Marketing.GetAllCampaigns(_remoteServiceBaseUrl, var uri = API.Marketing.GetAllCampaigns(_remoteServiceBaseUrl, pageSize, pageIndex);
pageSize, pageIndex);
var authorizationToken = await GetUserTokenAsync(); var responseString = await _httpClient.GetStringAsync(uri);
var dataString = await _apiClient.GetStringAsync(allCampaignItemsUri, authorizationToken);
var response = JsonConvert.DeserializeObject<Campaign>(dataString); var response = JsonConvert.DeserializeObject<Campaign>(responseString);
return response; return response;
} }
public async Task<CampaignItem> GetCampaignById(int id) public async Task<CampaignItem> GetCampaignById(int id)
{ {
var campaignByIdItemUri = API.Marketing.GetAllCampaignById(_remoteServiceBaseUrl, id); var uri = API.Marketing.GetAllCampaignById(_remoteServiceBaseUrl, id);
var authorizationToken = await GetUserTokenAsync(); var responseString = await _httpClient.GetStringAsync(uri);
var dataString = await _apiClient.GetStringAsync(campaignByIdItemUri, authorizationToken);
var response = JsonConvert.DeserializeObject<CampaignItem>(dataString); var response = JsonConvert.DeserializeObject<CampaignItem>(responseString);
return response; return response;
} }
private string GetUserIdentity()
{
return _httpContextAccesor.HttpContext.User.FindFirst("sub").Value;
}
private async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccesor.HttpContext;
return await context.GetTokenAsync("access_token");
}
} }
} }

View File

@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WebMVC.Infrastructure; using WebMVC.Infrastructure;
@ -13,16 +13,16 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
{ {
public class CatalogService : ICatalogService public class CatalogService : ICatalogService
{ {
private readonly IOptionsSnapshot<AppSettings> _settings; private readonly IOptions<AppSettings> _settings;
private readonly IHttpClient _apiClient; private readonly HttpClient _httpClient;
private readonly ILogger<CatalogService> _logger; private readonly ILogger<CatalogService> _logger;
private readonly string _remoteServiceBaseUrl; private readonly string _remoteServiceBaseUrl;
public CatalogService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient, ILogger<CatalogService> logger) public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<AppSettings> settings)
{ {
_httpClient = httpClient;
_settings = settings; _settings = settings;
_apiClient = httpClient;
_logger = logger; _logger = logger;
_remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1/c/catalog/"; _remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1/c/catalog/";
@ -30,25 +30,26 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type) public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type)
{ {
var allcatalogItemsUri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type); var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type);
var dataString = await _apiClient.GetStringAsync(allcatalogItemsUri); var responseString = await _httpClient.GetStringAsync(uri);
var response = JsonConvert.DeserializeObject<Catalog>(dataString); var catalog = JsonConvert.DeserializeObject<Catalog>(responseString);
return response; return catalog;
} }
public async Task<IEnumerable<SelectListItem>> GetBrands() public async Task<IEnumerable<SelectListItem>> GetBrands()
{ {
var getBrandsUri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl); var uri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl);
var dataString = await _apiClient.GetStringAsync(getBrandsUri); var responseString = await _httpClient.GetStringAsync(uri);
var items = new List<SelectListItem>(); var items = new List<SelectListItem>();
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
var brands = JArray.Parse(dataString); var brands = JArray.Parse(responseString);
foreach (var brand in brands.Children<JObject>()) foreach (var brand in brands.Children<JObject>())
{ {
@ -64,14 +65,14 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public async Task<IEnumerable<SelectListItem>> GetTypes() public async Task<IEnumerable<SelectListItem>> GetTypes()
{ {
var getTypesUri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl); var uri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl);
var dataString = await _apiClient.GetStringAsync(getTypesUri); var responseString = await _httpClient.GetStringAsync(uri);
var items = new List<SelectListItem>(); var items = new List<SelectListItem>();
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
var brands = JArray.Parse(dataString); var brands = JArray.Parse(responseString);
foreach (var brand in brands.Children<JObject>()) foreach (var brand in brands.Children<JObject>())
{ {
items.Add(new SelectListItem() items.Add(new SelectListItem()
@ -80,6 +81,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
Text = brand.Value<string>("type") Text = brand.Value<string>("type")
}); });
} }
return items; return items;
} }
} }

View File

@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.eShopOnContainers.WebMVC;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC;
using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using Newtonsoft.Json;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WebMVC.Infrastructure; using WebMVC.Infrastructure;
using WebMVC.Models; using WebMVC.Models;
@ -14,36 +12,27 @@ namespace WebMVC.Services
{ {
public class LocationService : ILocationService public class LocationService : ILocationService
{ {
private readonly IOptionsSnapshot<AppSettings> _settings; private readonly IOptions<AppSettings> _settings;
private readonly IHttpClient _apiClient; private readonly HttpClient _httpClient;
private readonly ILogger<CampaignService> _logger; private readonly ILogger<CampaignService> _logger;
private readonly string _remoteServiceBaseUrl; private readonly string _remoteServiceBaseUrl;
private readonly IHttpContextAccessor _httpContextAccesor;
public LocationService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient, public LocationService(HttpClient httpClient, IOptions<AppSettings> settings, ILogger<CampaignService> logger)
ILogger<CampaignService> logger, IHttpContextAccessor httpContextAccesor)
{ {
_httpClient = httpClient;
_settings = settings; _settings = settings;
_apiClient = httpClient;
_logger = logger; _logger = logger;
_remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/l/locations/"; _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/l/locations/";
_httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor));
} }
public async Task CreateOrUpdateUserLocation(LocationDTO location) public async Task CreateOrUpdateUserLocation(LocationDTO location)
{ {
var createOrUpdateUserLocationUri = API.Locations.CreateOrUpdateUserLocation(_remoteServiceBaseUrl); var uri = API.Locations.CreateOrUpdateUserLocation(_remoteServiceBaseUrl);
var locationContent = new StringContent(JsonConvert.SerializeObject(location), System.Text.Encoding.UTF8, "application/json");
var authorizationToken = await GetUserTokenAsync(); var response = await _httpClient.PostAsync(uri, locationContent);
var response = await _apiClient.PostAsync(createOrUpdateUserLocationUri, location, authorizationToken);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
}
private async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccesor.HttpContext;
return await context.GetTokenAsync("access_token");
} }
} }
} }

View File

@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WebMVC.Infrastructure; using WebMVC.Infrastructure;
using WebMVC.Models; using WebMVC.Models;
@ -14,69 +12,54 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
{ {
public class OrderingService : IOrderingService public class OrderingService : IOrderingService
{ {
private IHttpClient _apiClient; private HttpClient _httpClient;
private readonly string _remoteServiceBaseUrl; private readonly string _remoteServiceBaseUrl;
private readonly IOptionsSnapshot<AppSettings> _settings; private readonly IOptions<AppSettings> _settings;
private readonly IHttpContextAccessor _httpContextAccesor;
public OrderingService(IOptionsSnapshot<AppSettings> settings, IHttpContextAccessor httpContextAccesor, IHttpClient httpClient)
public OrderingService(HttpClient httpClient, IOptions<AppSettings> settings)
{ {
_remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/api/v1/o/orders"; _httpClient = httpClient;
_settings = settings; _settings = settings;
_httpContextAccesor = httpContextAccesor;
_apiClient = httpClient; _remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/api/v1/o/orders";
} }
async public Task<Order> GetOrder(ApplicationUser user, string id) async public Task<Order> GetOrder(ApplicationUser user, string id)
{ {
var token = await GetUserTokenAsync(); var uri = API.Order.GetOrder(_remoteServiceBaseUrl, id);
var getOrderUri = API.Order.GetOrder(_remoteServiceBaseUrl, id);
var dataString = await _apiClient.GetStringAsync(getOrderUri, token); var responseString = await _httpClient.GetStringAsync(uri);
var response = JsonConvert.DeserializeObject<Order>(dataString); var response = JsonConvert.DeserializeObject<Order>(responseString);
return response; return response;
} }
async public Task<List<Order>> GetMyOrders(ApplicationUser user) async public Task<List<Order>> GetMyOrders(ApplicationUser user)
{ {
var token = await GetUserTokenAsync(); var uri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl);
var allMyOrdersUri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl);
var dataString = await _apiClient.GetStringAsync(allMyOrdersUri, token); var responseString = await _httpClient.GetStringAsync(uri);
var response = JsonConvert.DeserializeObject<List<Order>>(dataString);
var response = JsonConvert.DeserializeObject<List<Order>>(responseString);
return response; return response;
} }
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order)
{
order.City = user.City;
order.Street = user.Street;
order.State = user.State;
order.Country = user.Country;
order.ZipCode = user.ZipCode;
order.CardNumber = user.CardNumber;
order.CardHolderName = user.CardHolderName;
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1);
order.CardSecurityNumber = user.SecurityNumber;
return order;
}
async public Task CancelOrder(string orderId) async public Task CancelOrder(string orderId)
{ {
var token = await GetUserTokenAsync();
var order = new OrderDTO() var order = new OrderDTO()
{ {
OrderNumber = orderId OrderNumber = orderId
}; };
var cancelOrderUri = API.Order.CancelOrder(_remoteServiceBaseUrl); var uri = API.Order.CancelOrder(_remoteServiceBaseUrl);
var orderContent = new StringContent(JsonConvert.SerializeObject(order), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PutAsync(cancelOrderUri, order, token, Guid.NewGuid().ToString());
var response = await _httpClient.PutAsync(uri, orderContent);
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{ {
@ -88,15 +71,15 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
async public Task ShipOrder(string orderId) async public Task ShipOrder(string orderId)
{ {
var token = await GetUserTokenAsync();
var order = new OrderDTO() var order = new OrderDTO()
{ {
OrderNumber = orderId OrderNumber = orderId
}; };
var shipOrderUri = API.Order.ShipOrder(_remoteServiceBaseUrl); var uri = API.Order.ShipOrder(_remoteServiceBaseUrl);
var orderContent = new StringContent(JsonConvert.SerializeObject(order), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PutAsync(shipOrderUri, order, token, Guid.NewGuid().ToString()); var response = await _httpClient.PutAsync(uri, orderContent);
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{ {
@ -120,6 +103,22 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
destination.CardSecurityNumber = original.CardSecurityNumber; destination.CardSecurityNumber = original.CardSecurityNumber;
} }
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order)
{
order.City = user.City;
order.Street = user.Street;
order.State = user.State;
order.Country = user.Country;
order.ZipCode = user.ZipCode;
order.CardNumber = user.CardNumber;
order.CardHolderName = user.CardHolderName;
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1);
order.CardSecurityNumber = user.SecurityNumber;
return order;
}
public BasketDTO MapOrderToBasket(Order order) public BasketDTO MapOrderToBasket(Order order)
{ {
order.CardExpirationApiFormat(); order.CardExpirationApiFormat();
@ -140,18 +139,5 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
RequestId = order.RequestId RequestId = order.RequestId
}; };
} }
void SetFakeIdToProducts(Order order)
{
var id = 1;
order.OrderItems.ForEach(x => { x.ProductId = id; id++; });
}
async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccesor.HttpContext;
return await context.GetTokenAsync("access_token");
}
} }
} }

View File

@ -6,17 +6,18 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC.Infrastructure;
using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.HealthChecks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Polly;
using Polly.Extensions.Http;
using StackExchange.Redis; using StackExchange.Redis;
using System; using System;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using WebMVC.Infrastructure; using WebMVC.Infrastructure;
using WebMVC.Infrastructure.Middlewares; using WebMVC.Infrastructure.Middlewares;
using WebMVC.Services; using WebMVC.Services;
@ -32,107 +33,15 @@ namespace Microsoft.eShopOnContainers.WebMVC
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the IoC container.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
RegisterAppInsights(services); services.AddAppInsight(Configuration)
.AddHealthChecks(Configuration)
services.AddMvc(); .AddCustomMvc(Configuration)
.AddHttpClientServices(Configuration)
services.AddSession(); //.AddHttpClientLogging(Configuration) //Opt-in HttpClientLogging config
.AddCustomAuthentication(Configuration);
if (Configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
{
services.AddDataProtection(opts =>
{
opts.ApplicationDiscriminator = "eshop.webmvc";
})
.PersistKeysToRedis(ConnectionMultiplexer.Connect(Configuration["DPConnectionString"]), "DataProtection-Keys");
}
services.Configure<AppSettings>(Configuration);
services.AddHealthChecks(checks =>
{
var minutes = 1;
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddUrlCheck(Configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos
checks.AddUrlCheck(Configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(Configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes));
});
// Add application services.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<ICatalogService, CatalogService>();
services.AddTransient<IOrderingService, OrderingService>();
services.AddTransient<IBasketService, BasketService>();
services.AddTransient<ICampaignService, CampaignService>();
services.AddTransient<ILocationService, LocationService>();
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
{
services.AddSingleton<IResilientHttpClientFactory, ResilientHttpClientFactory>(sp =>
{
var logger = sp.GetRequiredService<ILogger<ResilientHttpClient>>();
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var retryCount = 6;
if (!string.IsNullOrEmpty(Configuration["HttpClientRetryCount"]))
{
retryCount = int.Parse(Configuration["HttpClientRetryCount"]);
}
var exceptionsAllowedBeforeBreaking = 5;
if (!string.IsNullOrEmpty(Configuration["HttpClientExceptionsAllowedBeforeBreaking"]))
{
exceptionsAllowedBeforeBreaking = int.Parse(Configuration["HttpClientExceptionsAllowedBeforeBreaking"]);
}
return new ResilientHttpClientFactory(logger, httpContextAccessor, exceptionsAllowedBeforeBreaking, retryCount);
});
services.AddSingleton<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
}
else
{
services.AddSingleton<IHttpClient, StandardHttpClient>();
}
var useLoadTest = Configuration.GetValue<bool>("UseLoadTest");
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
var callBackUrl = Configuration.GetValue<string>("CallBackUrl");
// Add Authentication services
services.AddAuthentication(options => {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options => {
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = identityUrl.ToString();
options.SignedOutRedirectUri = callBackUrl.ToString();
options.ClientId = useLoadTest ? "mvctest" : "mvc";
options.ClientSecret = "secret";
options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.RequireHttpsMetadata = false;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("orders");
options.Scope.Add("basket");
options.Scope.Add("marketing");
options.Scope.Add("locations");
options.Scope.Add("webshoppingagg");
options.Scope.Add("orders.signalrhub");
});
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -140,15 +49,12 @@ namespace Microsoft.eShopOnContainers.WebMVC
{ {
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddAzureWebAppDiagnostics(); loggerFactory.AddAzureWebAppDiagnostics();
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
app.UseBrowserLink();
} }
else else
{ {
@ -174,7 +80,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
{ {
app.UseMiddleware<ByPassAuthMiddleware>(); app.UseMiddleware<ByPassAuthMiddleware>();
} }
app.UseAuthentication(); app.UseAuthentication();
var log = loggerFactory.CreateLogger("identity"); var log = loggerFactory.CreateLogger("identity");
@ -192,23 +98,184 @@ namespace Microsoft.eShopOnContainers.WebMVC
template: "{controller=Error}/{action=Error}"); template: "{controller=Error}/{action=Error}");
}); });
} }
}
private void RegisterAppInsights(IServiceCollection services) static class ServiceCollectionExtensions
{
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddApplicationInsightsTelemetry(Configuration); services.AddApplicationInsightsTelemetry(configuration);
var orchestratorType = Configuration.GetValue<string>("OrchestratorType"); var orchestratorType = configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S") if (orchestratorType?.ToUpper() == "K8S")
{ {
// Enable K8s telemetry initializer // Enable K8s telemetry initializer
services.EnableKubernetes(); services.EnableKubernetes();
} }
if (orchestratorType?.ToUpper() == "SF") if (orchestratorType?.ToUpper() == "SF")
{ {
// Enable SF telemetry initializer // Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer()); new FabricTelemetryInitializer());
} }
return services;
}
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks(checks =>
{
var minutes = 1;
if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed))
{
minutes = minutesParsed;
}
checks.AddUrlCheck(configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos
checks.AddUrlCheck(configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes));
});
return services;
}
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<AppSettings>(configuration);
services.AddMvc();
services.AddSession();
if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
{
services.AddDataProtection(opts =>
{
opts.ApplicationDiscriminator = "eshop.webmvc";
})
.PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys");
}
return services;
}
// Adds all Http client services (like Service-Agents) using resilient Http requests based on HttpClient factory and Polly's policies
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
services.AddTransient<HttpClientRequestIdDelegatingHandler>();
//set 5 min as the lifetime for each HttpMessageHandler int the pool
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5));
//add http client services
services.AddHttpClient<IBasketService, BasketService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ICatalogService, CatalogService>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<IOrderingService, OrderingService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ICampaignService, CampaignService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ILocationService, LocationService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
//add custom application services
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
return services;
}
public static IServiceCollection AddHttpClientLogging(this IServiceCollection services, IConfiguration configuration)
{
services.AddLogging(b =>
{
b.AddFilter((category, level) => true); // Spam the world with logs.
// Add console logger so we can see all the logging produced by the client by default.
b.AddConsole(c => c.IncludeScopes = true);
// Add console logger
b.AddDebug();
});
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
var useLoadTest = configuration.GetValue<bool>("UseLoadTest");
var identityUrl = configuration.GetValue<string>("IdentityUrl");
var callBackUrl = configuration.GetValue<string>("CallBackUrl");
// Add Authentication services
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = identityUrl.ToString();
options.SignedOutRedirectUri = callBackUrl.ToString();
options.ClientId = useLoadTest ? "mvctest" : "mvc";
options.ClientSecret = "secret";
options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.RequireHttpsMetadata = false;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("orders");
options.Scope.Add("basket");
options.Scope.Add("marketing");
options.Scope.Add("locations");
options.Scope.Add("webshoppingagg");
options.Scope.Add("orders.signalrhub");
});
return services;
}
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
} }
} }
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<UserSecretsId>aspnet-Microsoft.eShopOnContainers-946ae052-8305-4a99-965b-ec8636ddbae3</UserSecretsId> <UserSecretsId>aspnet-Microsoft.eShopOnContainers-946ae052-8305-4a99-965b-ec8636ddbae3</UserSecretsId>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
@ -20,11 +20,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BuildBundlerMinifier" Version="2.6.375" /> <PackageReference Include="BuildBundlerMinifier" Version="2.6.375" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.1" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.3" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Fabric.MSBuild" Version="1.6.5" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Fabric.MSBuild" Version="1.6.5" />
</ItemGroup> </ItemGroup>
@ -34,15 +36,13 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.5.357" /> <DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.7.385" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
<ProjectReference Include="..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,12 +1,24 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS sdk-with-node
ENV NODE_VERSION 8.11.1
ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60
RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \
&& echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \
&& tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \
&& rm nodejs.tar.gz \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
FROM sdk-with-node AS updated-npm
RUN npm i -g npm
FROM updated-npm as build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Web/WebSPA WORKDIR /src/src/Web/WebSPA
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<UserSecretsId>aspnetcorespa-c23d27a4-eb88-4b18-9b77-2a93f3b15119</UserSecretsId> <UserSecretsId>aspnetcorespa-c23d27a4-eb88-4b18-9b77-2a93f3b15119</UserSecretsId>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<TypeScriptCompileOnSaveEnabled>false</TypeScriptCompileOnSaveEnabled> <TypeScriptCompileOnSaveEnabled>false</TypeScriptCompileOnSaveEnabled>
@ -85,12 +85,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.1" /> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" /> <PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup> </ItemGroup>
<!-- workaround for https://github.com/aspnet/websdk/issues/114 --> <!-- workaround for https://github.com/aspnet/websdk/issues/114 -->
@ -103,11 +104,6 @@
</ItemGroup> </ItemGroup>
</Target> </Target>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
</ItemGroup>
<!-- workaround for https://github.com/aspnet/websdk/issues/114 --> <!-- workaround for https://github.com/aspnet/websdk/issues/114 -->
<!-- <!--
@ -117,7 +113,7 @@
</ItemGroup> </ItemGroup>
</Target> </Target>
--> -->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
@ -187,6 +183,10 @@
<TypeScriptCompile Include="Client\typings.d.ts" /> <TypeScriptCompile Include="Client\typings.d.ts" />
</ItemGroup> </ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties package-lock_1json__JSONSchema="http://json.schemastore.org/bower" /></VisualStudio></ProjectExtensions> <ProjectExtensions>
<VisualStudio>
<UserProperties package-lock_1json__JSONSchema="http://json.schemastore.org/bower" />
</VisualStudio>
</ProjectExtensions>
</Project> </Project>

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
FROM microsoft/aspnetcore:2.0.5 AS base FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/Web/WebStatus WORKDIR /src/src/Web/WebStatus
RUN dotnet restore -nowarn:msb3202,nu1503
RUN dotnet build --no-restore -c Release -o /app RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish FROM build AS publish

View File

@ -62,7 +62,6 @@ namespace WebStatus
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
app.UseBrowserLink();
} }
else else
{ {

View File

@ -1,19 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> <PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> <PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0" />
</ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> <ProjectReference Include="..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />

View File

@ -1,9 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<!--<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>-->
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
@ -44,8 +43,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.1" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.0" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup> </ItemGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>IntegrationTests</AssemblyName> <AssemblyName>IntegrationTests</AssemblyName>
<PackageId>IntegrationTests</PackageId> <PackageId>IntegrationTests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
@ -44,13 +44,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="Moq" Version="4.8.1" /> <PackageReference Include="Moq" Version="4.8.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.1" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.0" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" /> <PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="StackExchange.Redis.strongname" Version="1.2.6" /> <PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -94,7 +94,7 @@ namespace UnitTest.Ordering.Application
{ {
//Arrange //Arrange
var fakeDynamicResult = Enumerable.Empty<OrderSummary>(); var fakeDynamicResult = Enumerable.Empty<OrderSummary>();
_orderQueriesMock.Setup(x => x.GetOrdersAsync()) _orderQueriesMock.Setup(x => x.GetOrdersFromUserAsync(Guid.NewGuid()))
.Returns(Task.FromResult(fakeDynamicResult)); .Returns(Task.FromResult(fakeDynamicResult));
//Act //Act

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
@ -21,12 +21,12 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" /> <PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="MediatR" Version="4.0.1" /> <PackageReference Include="MediatR" Version="4.1.0" />
<PackageReference Include="Moq" Version="4.8.1" /> <PackageReference Include="Moq" Version="4.8.1" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.1" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>