Merge pull request #1 from dotnet-architecture/dev

merge origin to fork
This commit is contained in:
Dmytro Hridin 2019-02-14 19:36:55 +02:00 committed by GitHub
commit 051340a7cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
169 changed files with 39170 additions and 68 deletions

View File

@ -1,6 +1,9 @@
# eShopOnContainers - Microservices Architecture and Containers based Reference Application (**BETA state** - Visual Studio 2017 and CLI environments compatible)
Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers.
[![Build status (Linux images)](https://msftdevtools.visualstudio.com/eShopOnContainers/_apis/build/status/All%20Microservices%20Linux)](https://msftdevtools.visualstudio.com/eShopOnContainers/_build/latest?definitionId=184)
## IMPORTANT NOTES!
**You can use either the latest version of Visual Studio or simply Docker CLI and .NET CLI for Windows, Mac and Linux**.

View File

@ -14,7 +14,7 @@ services:
image: mongo
identity-api-test:
image: eshop/identity-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/identity-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Identity/Identity.API/Dockerfile
@ -22,7 +22,7 @@ services:
- sql-data-test
basket-api-test:
image: eshop/basket-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/basket-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Basket/Basket.API/Dockerfile
@ -35,7 +35,7 @@ services:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
basket-api-unit-test:
image: eshop/basket-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/basket-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Basket/Basket.API/Dockerfile
@ -48,7 +48,7 @@ services:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
catalog-api-test:
image: eshop/catalog-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/catalog-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
@ -60,7 +60,7 @@ services:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
catalog-api-unit-test:
image: eshop/catalog-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/catalog-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
@ -72,7 +72,7 @@ services:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
ordering-api-test:
image: eshop/ordering-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/ordering-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.API/Dockerfile
@ -84,7 +84,7 @@ services:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
ordering-api-unit-test:
image: eshop/ordering-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/ordering-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.API/Dockerfile
@ -96,7 +96,7 @@ services:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
marketing-api-test:
image: eshop/marketing-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/marketing-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Marketing/Marketing.API/Dockerfile
@ -110,7 +110,7 @@ services:
- ${BUILD_ARTIFACTSTAGINGDIRECTORY:-./tests-results/}:/tests
payment-api-test:
image: eshop/payment-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/payment-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Payment/Payment.API/Dockerfile
@ -118,7 +118,7 @@ services:
- rabbitmq-test
locations-api-test:
image: eshop/locations-api-test:${TAG:-latest}
image: ${REGISTRY:-eshop}/locations-api-test:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Location/Locations.API/Dockerfile

View File

@ -41,6 +41,8 @@ services:
- OrderingApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
- MobileShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5120
- WebShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5121
- WebhooksApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5113
- WebhooksWebClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5114
- UseCustomizationData=True
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
@ -182,6 +184,19 @@ services:
ports:
- "5109:80" # Important: In a production environment your should remove the external port (5109) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
webhooks.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=${ESHOP_AZURE_WEBHOOKS_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.WebhooksDb;User Id=sa;Password=Pass@word}
- EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq}
- EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
- EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
- IdentityUrl=http://identity.api
- IdentityUrlExternal=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105
ports:
- "5113:80" # Important: In a production environment your should remove the external port (5109) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
mobileshoppingapigw:
environment:
@ -380,3 +395,13 @@ services:
ports:
- "5100:80"
webhooks.client:
environment:
- ASPNETCORE_URLS=http://0.0.0.0:80
- Token=6168DB8D-DC58-4094-AF24-483278923590 # Webhooks are registered with this token (any value is valid) but the client won't check it
- IdentityUrl=http://10.0.75.1:5105
- CallBackUrl=http://localhost:5114
- WebhooksUrl=http://webhooks.api
- SelfUrl=http://webhooks.client/
ports:
- "5114:80"

View File

@ -14,7 +14,7 @@ services:
image: rabbitmq:3-management-alpine
identity.api:
image: eshop/identity.api:${TAG:-latest}
image: ${REGISTRY:-eshop}/identity.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Identity/Identity.API/Dockerfile
@ -22,7 +22,7 @@ services:
- sql.data
basket.api:
image: eshop/basket.api:${TAG:-latest}
image: ${REGISTRY:-eshop}/basket.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Basket/Basket.API/Dockerfile
@ -32,7 +32,7 @@ services:
- rabbitmq
catalog.api:
image: eshop/catalog.api:${TAG:-latest}
image: ${REGISTRY:-eshop}/catalog.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Catalog/Catalog.API/Dockerfile
@ -41,7 +41,7 @@ services:
- rabbitmq
ordering.api:
image: eshop/ordering.api:${TAG:-latest}
image: ${REGISTRY:-eshop}/ordering.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.API/Dockerfile
@ -50,7 +50,7 @@ services:
- rabbitmq
ordering.backgroundtasks:
image: eshop/ordering.backgroundtasks:${TAG:-latest}
image: ${REGISTRY:-eshop}/ordering.backgroundtasks:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile
@ -59,7 +59,7 @@ services:
- rabbitmq
marketing.api:
image: eshop/marketing.api:${TAG:-latest}
image: ${REGISTRY:-eshop}/marketing.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Marketing/Marketing.API/Dockerfile
@ -70,7 +70,7 @@ services:
- rabbitmq
payment.api:
image: eshop/payment.api:${TAG:-latest}
image: ${REGISTRY:-eshop}/payment.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Payment/Payment.API/Dockerfile
@ -78,7 +78,7 @@ services:
- rabbitmq
locations.api:
image: eshop/locations.api:${TAG:-latest}
image: ${REGISTRY:-eshop}/locations.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Location/Locations.API/Dockerfile
@ -86,8 +86,16 @@ services:
- nosql.data
- rabbitmq
webhooks.api:
image: ${REGISTRY:-eshop}/webhooks.api:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Webhooks/Webhooks.API/Dockerfile
depends_on:
- sql.data
mobileshoppingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@ -102,7 +110,7 @@ services:
- basket.api
mobilemarketingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@ -117,7 +125,7 @@ services:
- basket.api
webshoppingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@ -132,7 +140,7 @@ services:
- basket.api
webmarketingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
image: ${REGISTRY:-eshop}/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
@ -147,7 +155,7 @@ services:
- basket.api
mobileshoppingagg:
image: eshop/mobileshoppingagg:${TAG:-latest}
image: ${REGISTRY:-eshop}/mobileshoppingagg:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
@ -162,7 +170,7 @@ services:
- basket.api
webshoppingagg:
image: eshop/webshoppingagg:${TAG:-latest}
image: ${REGISTRY:-eshop}/webshoppingagg:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile
@ -177,7 +185,7 @@ services:
- basket.api
ordering.signalrhub:
image: eshop/ordering.signalrhub:${TAG:-latest}
image: ${REGISTRY:-eshop}/ordering.signalrhub:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.SignalrHub/Dockerfile
@ -192,13 +200,13 @@ services:
- basket.api
webstatus:
image: eshop/webstatus:${TAG:-latest}
image: ${REGISTRY:-eshop}/webstatus:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebStatus/Dockerfile
webspa:
image: eshop/webspa:${TAG:-latest}
image: ${REGISTRY:-eshop}/webspa:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebSPA/Dockerfile
@ -208,7 +216,7 @@ services:
# - webmarketingapigw
webmvc:
image: eshop/webmvc:${TAG:-latest}
image: ${REGISTRY:-eshop}/webmvc:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebMVC/Dockerfile
@ -217,3 +225,10 @@ services:
- webshoppingapigw
- webmarketingapigw
webhooks.client:
image: ${REGISTRY:-eshop}/webhooks.client:${TAG:-latest}
build:
context: .
dockerfile: src/Web/WebhookClient/Dockerfile
depends_on:
- webhooks.api

View File

@ -140,6 +140,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Payment", "Payment", "{C61C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payment.API", "src\Services\Payment\Payment.API\Payment.API.csproj", "{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Webhooks", "Webhooks", "{E0AA11C4-2873-461D-8F82-53392530FB7A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webhooks.API", "src\Services\Webhooks\Webhooks.API\Webhooks.API.csproj", "{84E2016E-0435-44C6-8020-3D288AA38B2C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebhookClient", "src\Web\WebhookClient\WebhookClient.csproj", "{766D7E92-6AF0-476C-ADD5-282BF4D8C576}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@ -1642,6 +1648,102 @@ Global
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|x64.Build.0 = Release|Any CPU
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|x86.ActiveCfg = Release|Any CPU
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84}.Release|x86.Build.0 = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|ARM.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhone.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x64.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x64.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x86.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.AppStore|x86.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|ARM.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhone.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x64.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x64.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x86.ActiveCfg = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Debug|x86.Build.0 = Debug|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|Any CPU.Build.0 = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|ARM.ActiveCfg = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|ARM.Build.0 = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhone.ActiveCfg = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhone.Build.0 = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x64.ActiveCfg = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x64.Build.0 = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x86.ActiveCfg = Release|Any CPU
{84E2016E-0435-44C6-8020-3D288AA38B2C}.Release|x86.Build.0 = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|ARM.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhone.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x64.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x64.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x86.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.AppStore|x86.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|ARM.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|ARM.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhone.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x64.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x64.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x86.ActiveCfg = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Debug|x86.Build.0 = Debug|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|Any CPU.Build.0 = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|ARM.ActiveCfg = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|ARM.Build.0 = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhone.ActiveCfg = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhone.Build.0 = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x64.ActiveCfg = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x64.Build.0 = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x86.ActiveCfg = Release|Any CPU
{766D7E92-6AF0-476C-ADD5-282BF4D8C576}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1703,6 +1805,9 @@ Global
{120CABB3-0FEA-4B40-B4B5-2D3041798C80} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B}
{C61C5CFE-4876-4A46-A96E-5BBF596A984A} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{0AB40131-8AD7-436F-9C6B-EDA59CFA3A84} = {C61C5CFE-4876-4A46-A96E-5BBF596A984A}
{E0AA11C4-2873-461D-8F82-53392530FB7A} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8}
{84E2016E-0435-44C6-8020-3D288AA38B2C} = {E0AA11C4-2873-461D-8F82-53392530FB7A}
{766D7E92-6AF0-476C-ADD5-282BF4D8C576} = {E279BF0F-7F66-4F3A-A3AB-2CDA66C1CD04}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9}

View File

@ -36,6 +36,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:

View File

@ -50,4 +50,15 @@ env:
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -36,6 +36,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:

View File

@ -50,4 +50,15 @@ env:
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -36,6 +36,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:

View File

@ -50,4 +50,15 @@ env:
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -6,7 +6,6 @@ kind: Deployment
metadata:
name: {{ template "apigwws.fullname" . }}
labels:
ufo: {{ $cfgname}}
app: {{ template "apigwws.name" . }}
chart: {{ template "apigwws.chart" . }}
release: {{ .Release.Name }}
@ -36,6 +35,27 @@ spec:
path: configuration.json
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:

View File

@ -50,4 +50,15 @@ env:
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -22,6 +22,8 @@ app: # app global settings
payment: payment-api # ingress entry for payment api
locations: locations-api # ingress entry for locations api
marketing: marketing-api # ingress entry for marketing api
webhooks: webhooks-api # ingress entry for webhooks api
webhooksweb: webhooks-web # ingress entry for webhooks web demo client
svc:
basket: basket # service name for basket api
catalog: catalog # service name for catalog api
@ -41,3 +43,5 @@ app: # app global settings
payment: payment # service name for payment api
locations: locations # service name for locations api
marketing: marketing # service name for marketing ap
webhooks: webhooks # service name for webhooks api
webhooksweb: webhooksweb # service name for webhooks web

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -41,3 +41,15 @@ env:
values:
- name: OrchestratorType
value: 'K8S'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -44,4 +44,16 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -61,7 +61,7 @@ if (-not [string]::IsNullOrEmpty($registry)) {
Write-Host "Begin eShopOnContainers installation using Helm" -ForegroundColor Green
$infras = ("sql-data", "nosql-data", "rabbitmq", "keystore-data", "basket-data")
$charts = ("eshop-common", "apigwmm", "apigwms", "apigwwm", "apigwws", "basket-api","catalog-api", "identity-api", "locations-api", "marketing-api", "mobileshoppingagg","ordering-api","ordering-backgroundtasks","ordering-signalrhub", "payment-api", "webmvc", "webshoppingagg", "webspa", "webstatus")
$charts = ("eshop-common", "apigwmm", "apigwms", "apigwwm", "apigwws", "basket-api","catalog-api", "identity-api", "locations-api", "marketing-api", "mobileshoppingagg","ordering-api","ordering-backgroundtasks","ordering-signalrhub", "payment-api", "webmvc", "webshoppingagg", "webspa", "webstatus", "webhooks-api", "webhooks-web")
if ($deployInfrastructure) {
foreach ($infra in $infras) {

View File

@ -9,6 +9,8 @@
{{- $mobileshoppingagg := include "url-of" (list .Values.app.ingress.entries.mobileshoppingagg .) -}}
{{- $webhoppingagg := include "url-of" (list .Values.app.ingress.entries.webshoppingagg .) -}}
{{- $xamarincallback := include "url-of" (list "xamarincallback" .) -}}
{{- $webhooks_url := include "url-of" (list .Values.app.ingress.entries.webhooks .) -}}
{{- $webhooksweb_url := include "url-of" (list .Values.app.ingress.entries.webhooksweb .) -}}
apiVersion: v1
kind: ConfigMap
@ -32,4 +34,7 @@ data:
mobileshoppingagg_e: http://{{ $mobileshoppingagg }}
webshoppingagg_e: http://{{ $webhoppingagg }}
xamarin_callback_e: http://{{ $xamarincallback }}
webhooksapi_e: http://{{ $webhooks_url }}
webhooksweb_e: http://{{ $webhooksweb_url }}

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -54,6 +54,10 @@ env:
key: webshoppingagg_e
- name: XamarinCallback
key: xamarin_callback_e
- name: WebhooksApiClient
key: webhooksapi_e
- name: WebhooksWebClient
key: webhooksweb_e
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
@ -61,4 +65,15 @@ env:
value: 'K8S'
- name: IsClusterEnv
value: 'True'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -16,6 +16,8 @@ inf:
db: IdentityDb # Ordering API SQL db name
marketing:
db: MarketingDb # Marketing API SQL db name
webhooks:
db: WebhooksDb # Webhooks DB
mongo:
# host: my-nosql-data # Uncomment to use specify custom mongo host. By default nosql-data is used
locations:

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -52,4 +52,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -56,4 +56,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -64,4 +64,15 @@ env:
value: 'K8S'
- name: IsClusterEnv
value: 'True'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -50,4 +50,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -56,4 +56,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -42,4 +42,15 @@ env:
value: Development
- name: OrchestratorType
value: 'K8S'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: webhooks-api
version: 0.1.0

View File

@ -0,0 +1,8 @@
eShop Ordering API installed.
-----------------------------
This API is not directly exposed outside cluster. If need to access it use:
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "webhooks-api.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "webhooks-api.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "webhooks-api.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "webhooks-api.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,51 @@
{{- define "suffix-name" -}}
{{- if .Values.app.name -}}
{{- .Values.app.name -}}
{{- else -}}
{{- .Release.Name -}}
{{- end -}}
{{- end -}}
{{- define "sql-name" -}}
{{- if .Values.inf.sql.host -}}
{{- .Values.inf.sql.host -}}
{{- else -}}
{{- printf "%s" "sql-data" -}}
{{- end -}}
{{- end -}}
{{- define "mongo-name" -}}
{{- if .Values.inf.mongo.host -}}
{{- .Values.inf.mongo.host -}}
{{- else -}}
{{- printf "%s" "nosql-data" -}}
{{- end -}}
{{- end -}}
{{- define "url-of" -}}
{{- $name := first .}}
{{- $ctx := last .}}
{{- if eq $name "" -}}
{{- $ctx.Values.inf.k8s.dns -}}
{{- else -}}
{{- printf "%s/%s" $ctx.Values.inf.k8s.dns $name -}} {{/*Value is just <dns>/<name> */}}
{{- end -}}
{{- end -}}
{{- define "pathBase" -}}
{{- if .Values.inf.k8s.suffix -}}
{{- $suffix := include "suffix-name" . -}}
{{- printf "%s-%s" .Values.pathBase $suffix -}}
{{- else -}}
{{- .Values.pathBase -}}
{{- end -}}
{{- end -}}
{{- define "fqdn-image" -}}
{{- if .Values.inf.registry -}}
{{- printf "%s/%s" .Values.inf.registry.server .Values.image.repository -}}
{{- else -}}
{{- .Values.image.repository -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,20 @@
{{- $name := include "webhooks-api.fullname" . -}}
{{- $sqlsrv := include "sql-name" . -}}
{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: "cfg-{{ $name }}"
labels:
app: {{ template "webhooks-api.name" . }}
chart: {{ template "webhooks-api.chart" .}}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
data:
webhooks__ConnectionString: Server={{ $sqlsrv }};Initial Catalog={{ .Values.inf.sql.webhooks.db }};User Id={{ .Values.inf.sql.common.user }};Password={{ .Values.inf.sql.common.pwd }};
urls__IdentityUrl: http://{{ $identity }}
urls__IdentityUrlExternal: http://{{ $identity }}
all__EventBusConnection: {{ .Values.inf.eventbus.constr }}
all__InstrumentationKey: {{ .Values.inf.appinsights.key }}
all__UseAzureServiceBus: "{{ .Values.inf.eventbus.useAzure }}"

View File

@ -0,0 +1,71 @@
{{- $name := include "webhooks-api.fullname" . -}}
{{- $cfgname := printf "%s-%s" "cfg" $name -}}
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "webhooks-api.fullname" . }}
labels:
ufo: {{ $cfgname}}
app: {{ template "webhooks-api.name" . }}
chart: {{ template "webhooks-api.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "webhooks-api.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "webhooks-api.name" . }}
release: {{ .Release.Name }}
spec:
{{ if .Values.inf.registry -}}
imagePullSecrets:
- name: {{ .Values.inf.registry.secretName }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: PATH_BASE
value: {{ include "pathBase" . }}
- name: k8sname
value: {{ .Values.clusterName }}
{{- if .Values.env.values -}}
{{- range .Values.env.values }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end -}}
{{- end -}}
{{- if .Values.env.configmap -}}
{{- range .Values.env.configmap }}
- name: {{ .name }}
valueFrom:
configMapKeyRef:
name: {{ $cfgname }}
key: {{ .key }}
{{- end -}}
{{- end }}
ports:
- name: http
containerPort: 80
protocol: TCP
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}

View File

@ -0,0 +1,33 @@
{{- if .Values.ingress.enabled -}}
{{- $ingressPath := include "pathBase" . -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "webhooks-api.fullname" . }}
labels:
app: {{ template "webhooks-api.name" . }}
chart: {{ template "webhooks-api.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
- {{ .Values.inf.k8s.dns }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
- host: {{ .Values.inf.k8s.dns }}
http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ .Values.app.svc.webhooks }}
servicePort: http
{{- end }}

View File

@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.app.svc.webhooks }}
labels:
app: {{ template "webhooks-api.name" . }}
chart: {{ template "webhooks-api.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app: {{ template "webhooks-api.name" . }}
release: {{ .Release.Name }}

View File

@ -0,0 +1,53 @@
replicaCount: 1
clusterName: eshop-aks
pathBase: /webhooks-api
image:
repository: eshop/webhooks.api
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
annotations: {}
hosts:
- chart-example.local
tls: []
resources: {}
nodeSelector: {}
tolerations: []
affinity: {}
# env defines the environment variables that will be declared in the pod
env:
urls:
# configmap declares variables which value is taken from the config map defined in template configmap.yaml (name is name of var and key the key in configmap).
configmap:
- name: ConnectionString
key: webhooks__ConnectionString
- name: ApplicationInsights__InstrumentationKey
key: all__InstrumentationKey
- name: EventBusConnection
key: all__EventBusConnection
- name: AzureServiceBusEnabled
key: all__UseAzureServiceBus
- name: IdentityUrl
key: urls__IdentityUrl
- name: IdentityUrlExternal
key: urls__IdentityUrlExternal
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
value: Development
- name: OrchestratorType
value: 'K8S'

View File

@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: webhooks-web
version: 0.1.0

View File

@ -0,0 +1,8 @@
eShop Ordering API installed.
-----------------------------
This API is not directly exposed outside cluster. If need to access it use:
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "webhooks-web.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "webhooks-web.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "webhooks-web.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "webhooks-web.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,51 @@
{{- define "suffix-name" -}}
{{- if .Values.app.name -}}
{{- .Values.app.name -}}
{{- else -}}
{{- .Release.Name -}}
{{- end -}}
{{- end -}}
{{- define "sql-name" -}}
{{- if .Values.inf.sql.host -}}
{{- .Values.inf.sql.host -}}
{{- else -}}
{{- printf "%s" "sql-data" -}}
{{- end -}}
{{- end -}}
{{- define "mongo-name" -}}
{{- if .Values.inf.mongo.host -}}
{{- .Values.inf.mongo.host -}}
{{- else -}}
{{- printf "%s" "nosql-data" -}}
{{- end -}}
{{- end -}}
{{- define "url-of" -}}
{{- $name := first .}}
{{- $ctx := last .}}
{{- if eq $name "" -}}
{{- $ctx.Values.inf.k8s.dns -}}
{{- else -}}
{{- printf "%s/%s" $ctx.Values.inf.k8s.dns $name -}} {{/*Value is just <dns>/<name> */}}
{{- end -}}
{{- end -}}
{{- define "pathBase" -}}
{{- if .Values.inf.k8s.suffix -}}
{{- $suffix := include "suffix-name" . -}}
{{- printf "%s-%s" .Values.pathBase $suffix -}}
{{- else -}}
{{- .Values.pathBase -}}
{{- end -}}
{{- end -}}
{{- define "fqdn-image" -}}
{{- if .Values.inf.registry -}}
{{- printf "%s/%s" .Values.inf.registry.server .Values.image.repository -}}
{{- else -}}
{{- .Values.image.repository -}}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,19 @@
{{- $name := include "webhooks-web.fullname" . -}}
{{- $identity := include "url-of" (list .Values.app.ingress.entries.identity .) -}}
{{- $webhooksweb := include "url-of" (list .Values.app.ingress.entries.webhooksweb .) -}}
{{- $webhooks := include "url-of" (list .Values.app.ingress.entries.webhooks .) -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: "cfg-{{ $name }}"
labels:
app: {{ template "webhooks-web.name" . }}
chart: {{ template "webhooks-web.chart" .}}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
data:
urls__webhooks: http://{{ $webhooks }}
identity_e: http://{{ $identity }}
webhooksweb_e: http://{{ $webhooksweb }}
urls_webhooksweb: http://{{ .Values.app.svc.webhooksweb }}

View File

@ -0,0 +1,71 @@
{{- $name := include "webhooks-web.fullname" . -}}
{{- $cfgname := printf "%s-%s" "cfg" $name -}}
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "webhooks-web.fullname" . }}
labels:
ufo: {{ $cfgname}}
app: {{ template "webhooks-web.name" . }}
chart: {{ template "webhooks-web.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "webhooks-web.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "webhooks-web.name" . }}
release: {{ .Release.Name }}
spec:
{{ if .Values.inf.registry -}}
imagePullSecrets:
- name: {{ .Values.inf.registry.secretName }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: PATH_BASE
value: {{ include "pathBase" . }}
- name: k8sname
value: {{ .Values.clusterName }}
{{- if .Values.env.values -}}
{{- range .Values.env.values }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end -}}
{{- end -}}
{{- if .Values.env.configmap -}}
{{- range .Values.env.configmap }}
- name: {{ .name }}
valueFrom:
configMapKeyRef:
name: {{ $cfgname }}
key: {{ .key }}
{{- end -}}
{{- end }}
ports:
- name: http
containerPort: 80
protocol: TCP
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}

View File

@ -0,0 +1,33 @@
{{- if .Values.ingress.enabled -}}
{{- $ingressPath := include "pathBase" . -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "webhooks-web.fullname" . }}
labels:
app: {{ template "webhooks-web.name" . }}
chart: {{ template "webhooks-web.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
- {{ .Values.inf.k8s.dns }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
- host: {{ .Values.inf.k8s.dns }}
http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ .Values.app.svc.webhooksweb }}
servicePort: http
{{- end }}

View File

@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.app.svc.webhooksweb }}
labels:
app: {{ template "webhooks-web.name" . }}
chart: {{ template "webhooks-web.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app: {{ template "webhooks-web.name" . }}
release: {{ .Release.Name }}

View File

@ -0,0 +1,52 @@
replicaCount: 1
clusterName: eshop-aks
pathBase: /webhooks-web
image:
repository: eshop/webhooks.client
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
annotations: {}
hosts:
- chart-example.local
tls: []
resources: {}
nodeSelector: {}
tolerations: []
affinity: {}
# env defines the environment variables that will be declared in the pod
env:
urls:
# configmap declares variables which value is taken from the config map defined in template configmap.yaml (name is name of var and key the key in configmap).
configmap:
- name: WebhooksUrl
key: urls__webhooks
- name: IdentityUrl
key: identity_e
- name: CallbackUrl
key: webhooksweb_e
- name: SelfUrl
key: webhooksweb_e
# values define environment variables with a fixed value (no configmap involved) (name is name of var, and value is its value)
values:
- name: ASPNETCORE_ENVIRONMENT
value: Production
- name: OrchestratorType
value: 'K8S'
- name: Token
value: "WebHooks-Demo-Web" # Can use whatever you want

View File

@ -28,6 +28,27 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
{{ if .Values.probes -}}
{{- if .Values.probes.liveness -}}
livenessProbe:
httpGet:
port: {{ .Values.probes.liveness.port }}
path: {{ .Values.probes.liveness.path }}
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
{{- end -}}
{{- end -}}
{{- if .Values.probes -}}
{{- if .Values.probes.readiness }}
readinessProbe:
httpGet:
port: {{ .Values.probes.readiness.port }}
path: {{ .Values.probes.readiness.path }}
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
{{- end -}}
{{- end }}
image: "{{ template "fqdn-image" . }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:

View File

@ -64,4 +64,16 @@ env:
value: 'K8S'
- name: IsClusterEnv
value: 'True'
probes:
liveness:
path: /liveness
initialDelaySeconds: 10
periodSeconds: 15
port: 80
readiness:
path: /hc
timeoutSeconds: 5
initialDelaySeconds: 90
periodSeconds: 60
port: 80

View File

@ -6,7 +6,7 @@
<ItemGroup>
<PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0-preview3-35497" />
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Polly;
@ -10,8 +11,17 @@ namespace Microsoft.AspNetCore.Hosting
{
public static class IWebHostExtensions
{
public static bool IsInKubernetes(this IWebHost webHost)
{
var cfg = webHost.Services.GetService<IConfiguration>();
var orchestratorType = cfg.GetValue<string>("OrchestratorType");
return orchestratorType?.ToUpper() == "K8S";
}
public static IWebHost MigrateDbContext<TContext>(this IWebHost webHost, Action<TContext,IServiceProvider> seeder) where TContext : DbContext
{
var underK8s = webHost.IsInKubernetes();
using (var scope = webHost.Services.CreateScope())
{
var services = scope.ServiceProvider;
@ -24,36 +34,47 @@ namespace Microsoft.AspNetCore.Hosting
{
logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}");
var retry = Policy.Handle<SqlException>()
.WaitAndRetry(new TimeSpan[]
{
if (underK8s)
{
InvokeSeeder(seeder, context, services);
}
else
{
var retry = Policy.Handle<SqlException>()
.WaitAndRetry(new TimeSpan[]
{
TimeSpan.FromSeconds(3),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(8),
});
});
retry.Execute(() =>
{
//if the sql server container is not created on run docker compose this
//migration can't fail for network related exception. The retry options for DbContext only
//apply to transient exceptions.
context.Database
.Migrate();
seeder(context, services);
});
//apply to transient exceptions
// Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service)
retry.Execute(() => InvokeSeeder(seeder, context, services));
}
logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}");
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred while migrating the database used on context {typeof(TContext).Name}");
if (underK8s)
{
throw; // Rethrow under k8s because we rely on k8s to re-run the pod
}
}
}
return webHost;
}
private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services)
where TContext : DbContext
{
context.Database.Migrate();
seeder(context, services);
}
}
}

View File

@ -17,7 +17,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
new ApiResource("locations", "Locations Service"),
new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"),
new ApiResource("webshoppingagg", "Web Shopping Aggregator"),
new ApiResource("orders.signalrhub", "Ordering Signalr Hub")
new ApiResource("orders.signalrhub", "Ordering Signalr Hub"),
new ApiResource("webhooks", "Webhooks registration Service"),
};
}
@ -33,7 +34,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl)
public static IEnumerable<Client> GetClients(Dictionary<string, string> clientsUrl)
{
return new List<Client>
{
@ -57,7 +58,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"locations",
"marketing",
"webshoppingagg",
"orders.signalrhub"
"orders.signalrhub",
"webhooks"
},
},
new Client
@ -74,7 +76,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
RequireConsent = false,
RequirePkce = true,
PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
AllowedCorsOrigins = { "http://eshopxamarin" },
//AllowedCorsOrigins = { "http://eshopxamarin" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
@ -84,7 +86,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"basket",
"locations",
"marketing",
"mobileshoppingagg"
"mobileshoppingagg",
"webhooks"
},
//Allow requesting refresh tokens for long lived API access
AllowOfflineAccess = true,
@ -122,7 +125,40 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"locations",
"marketing",
"webshoppingagg",
"orders.signalrhub"
"orders.signalrhub",
"webhooks"
},
AccessTokenLifetime = 60*60*2, // 2 hours
IdentityTokenLifetime= 60*60*2 // 2 hours
},
new Client
{
ClientId = "webhooksclient",
ClientName = "Webhooks Client",
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
ClientUri = $"{clientsUrl["WebhooksWeb"]}", // public uri of the client
AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = false,
RequireConsent = false,
AllowOfflineAccess = true,
AlwaysIncludeUserClaimsInIdToken = true,
RedirectUris = new List<string>
{
$"{clientsUrl["WebhooksWeb"]}/signin-oidc"
},
PostLogoutRedirectUris = new List<string>
{
$"{clientsUrl["WebhooksWeb"]}/signout-callback-oidc"
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"webhooks"
},
AccessTokenLifetime = 60*60*2, // 2 hours
IdentityTokenLifetime= 60*60*2 // 2 hours
@ -157,7 +193,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
"basket",
"locations",
"marketing",
"webshoppingagg"
"webshoppingagg",
"webhooks"
},
},
new Client
@ -249,6 +286,21 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration
{
"webshoppingagg"
}
},
new Client
{
ClientId = "webhooksswaggerui",
ClientName = "WebHooks Service Swagger UI",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{clientsUrl["WebhooksApi"]}/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { $"{clientsUrl["WebhooksApi"]}/swagger/" },
AllowedScopes =
{
"webhooks"
}
}
};
}

View File

@ -28,6 +28,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
clientUrls.Add("OrderingApi", configuration.GetValue<string>("OrderingApiClient"));
clientUrls.Add("MobileShoppingAgg", configuration.GetValue<string>("MobileShoppingAggClient"));
clientUrls.Add("WebShoppingAgg", configuration.GetValue<string>("WebShoppingAggClient"));
clientUrls.Add("WebhooksApi", configuration.GetValue<string>("WebhooksApiClient"));
clientUrls.Add("WebhooksWeb", configuration.GetValue<string>("WebhooksWebClient"));
if (!context.Clients.Any())
{

View File

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false">
<environmentVariables />
</aspNetCore>
</system.webServer>
</configuration>

View File

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc;
namespace Webhooks.API.Controllers
{
public class HomeController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return new RedirectResult("~/swagger");
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Webhooks.API.Model;
namespace Webhooks.API.Controllers
{
public class WebhookSubscriptionRequest : IValidatableObject
{
public string Url { get; set; }
public string Token { get; set; }
public string Event { get; set; }
public string GrantUrl { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!Uri.IsWellFormedUriString(GrantUrl, UriKind.Absolute))
{
yield return new ValidationResult("GrantUrl is not valid", new[] { nameof(GrantUrl) });
}
if (!Uri.IsWellFormedUriString(Url, UriKind.Absolute))
{
yield return new ValidationResult("Url is not valid", new[] { nameof(Url) });
}
var isOk = Enum.TryParse<WebhookType>(Event, ignoreCase: true, result: out WebhookType whtype);
if (!isOk)
{
yield return new ValidationResult($"{Event} is invalid event name", new[] { nameof(Event) });
}
}
}
}

View File

@ -0,0 +1,115 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Webhooks.API.Infrastructure;
using Webhooks.API.Model;
using Webhooks.API.Services;
namespace Webhooks.API.Controllers
{
[Route("api/v1/[controller]")]
[ApiController]
public class WebhooksController : ControllerBase
{
private readonly WebhooksContext _dbContext;
private readonly IIdentityService _identityService;
private readonly IGrantUrlTesterService _grantUrlTester;
public WebhooksController(WebhooksContext dbContext, IIdentityService identityService, IGrantUrlTesterService grantUrlTester)
{
_dbContext = dbContext;
_identityService = identityService;
_grantUrlTester = grantUrlTester;
}
[Authorize]
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<WebhookSubscription>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> ListByUser()
{
var userId = _identityService.GetUserIdentity();
var data = await _dbContext.Subscriptions.Where(s => s.UserId == userId).ToListAsync();
return Ok(data);
}
[Authorize]
[HttpGet("{id:int}")]
[ProducesResponseType(typeof(WebhookSubscription), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> GetByUserAndId(int id)
{
var userId = _identityService.GetUserIdentity();
var subscription = await _dbContext.Subscriptions.SingleOrDefaultAsync(s => s.Id == id && s.UserId == userId);
if (subscription != null)
{
return Ok(subscription);
}
return NotFound($"Subscriptions {id} not found");
}
[Authorize]
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.Created)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(418)]
public async Task<IActionResult> SubscribeWebhook(WebhookSubscriptionRequest request)
{
if (!ModelState.IsValid)
{
return ValidationProblem(ModelState);
}
var userId = _identityService.GetUserIdentity();
var grantOk = await _grantUrlTester.TestGrantUrl(request.Url, request.GrantUrl, request.Token ?? string.Empty);
if (grantOk)
{
var subscription = new WebhookSubscription()
{
Date = DateTime.UtcNow,
DestUrl = request.Url,
Token = request.Token,
Type = Enum.Parse<WebhookType>(request.Event, ignoreCase: true),
UserId = _identityService.GetUserIdentity()
};
_dbContext.Add(subscription);
await _dbContext.SaveChangesAsync();
return CreatedAtAction("GetByUserAndId", new { id = subscription.Id }, subscription);
}
else
{
return StatusCode(418, "Grant url can't be validated");
}
}
[Authorize]
[HttpDelete("{id:int}")]
[ProducesResponseType((int)HttpStatusCode.Accepted)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> UnsubscribeWebhook(int id)
{
var userId = _identityService.GetUserIdentity();
var subscription = await _dbContext.Subscriptions.SingleOrDefaultAsync(s => s.Id == id && s.UserId == userId);
if (subscription != null)
{
_dbContext.Remove(subscription);
await _dbContext.SaveChangesAsync();
return Accepted();
}
return NotFound($"Subscriptions {id} not found");
}
}
}

View File

@ -0,0 +1,19 @@
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY ["src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj", "src/Services/Webhooks/Webhooks.API/"]
RUN dotnet restore "src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj"
COPY . .
WORKDIR "/src/src/Services/Webhooks/Webhooks.API"
RUN dotnet build "Webhooks.API.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "Webhooks.API.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Webhooks.API.dll"]

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.Exceptions
{
public class WebhooksDomainException : Exception
{
}
}

View File

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Webhooks.API.Infrastructure.ActionResult
{
class InternalServerErrorObjectResult : ObjectResult
{
public InternalServerErrorObjectResult(object error) : base(error)
{
StatusCode = StatusCodes.Status500InternalServerError;
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.Infrastructure
{
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
// Check for authorize attribute
var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType<AuthorizeAttribute>().Any() ||
context.ApiDescription.ActionAttributes().OfType<AuthorizeAttribute>().Any();
if (hasAuthorize)
{
operation.Responses.Add("401", new Response { Description = "Unauthorized" });
operation.Responses.Add("403", new Response { Description = "Forbidden" });
operation.Security = new List<IDictionary<string, IEnumerable<string>>>();
operation.Security.Add(new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new [] { "webhooksapi" } }
});
}
}
}
}

View File

@ -0,0 +1,72 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Webhooks.API.Exceptions;
using Webhooks.API.Infrastructure.ActionResult;
namespace Webhooks.API.Infrastructure
{
public class HttpGlobalExceptionFilter : IExceptionFilter
{
private readonly IHostingEnvironment env;
private readonly ILogger<HttpGlobalExceptionFilter> logger;
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
{
this.env = env;
this.logger = logger;
}
public void OnException(ExceptionContext context)
{
logger.LogError(new EventId(context.Exception.HResult),
context.Exception,
context.Exception.Message);
if (context.Exception.GetType() == typeof(WebhooksDomainException))
{
var problemDetails = new ValidationProblemDetails()
{
Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
};
problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() });
context.Result = new BadRequestObjectResult(problemDetails);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
else
{
var json = new JsonErrorResponse
{
Messages = new[] { "An error ocurred." }
};
if (env.IsDevelopment())
{
json.DeveloperMeesage = context.Exception;
}
context.Result = new InternalServerErrorObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
context.ExceptionHandled = true;
}
private class JsonErrorResponse
{
public string[] Messages { get; set; }
public object DeveloperMeesage { get; set; }
}
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Webhooks.API.Model;
namespace Webhooks.API.Infrastructure
{
public class WebhooksContext : DbContext
{
public WebhooksContext(DbContextOptions<WebhooksContext> options) : base(options)
{
}
public DbSet<WebhookSubscription> Subscriptions { get; set; }
}
public class WebhooksContextDesignFactory : IDesignTimeDbContextFactory<WebhooksContext>
{
public WebhooksContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<WebhooksContext>()
.UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;Integrated Security=true");
return new WebhooksContext(optionsBuilder.Options);
}
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.IntegrationEvents
{
public class OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent
{
public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; }
public OrderStatusChangedToPaidIntegrationEvent(int orderId,
IEnumerable<OrderStockItem> orderStockItems)
{
OrderId = orderId;
OrderStockItems = orderStockItems;
}
}
public class OrderStockItem
{
public int ProductId { get; }
public int Units { get; }
public OrderStockItem(int productId, int units)
{
ProductId = productId;
Units = units;
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Webhooks.API.Model;
using Webhooks.API.Services;
using Microsoft.Extensions.Logging;
namespace Webhooks.API.IntegrationEvents
{
public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent>
{
private readonly IWebhooksRetriever _retriever;
private readonly IWebhooksSender _sender;
private readonly ILogger _logger;
public OrderStatusChangedToPaidIntegrationEventHandler(IWebhooksRetriever retriever, IWebhooksSender sender, ILogger<OrderStatusChangedToShippedIntegrationEventHandler> logger )
{
_retriever = retriever;
_sender = sender;
_logger = logger;
}
public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event)
{
var subscriptions = await _retriever.GetSubscriptionsOfType(WebhookType.OrderPaid);
_logger.LogInformation($"Received OrderStatusChangedToShippedIntegrationEvent and got {subscriptions.Count()} subscriptions to process");
var whook = new WebhookData(WebhookType.OrderPaid, @event);
await _sender.SendAll(subscriptions, whook);
}
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.IntegrationEvents
{
public class OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent
{
public int OrderId { get; private set; }
public string OrderStatus { get; private set; }
public string BuyerName { get; private set; }
public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName)
{
OrderId = orderId;
OrderStatus = orderStatus;
BuyerName = buyerName;
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Webhooks.API.Model;
using Webhooks.API.Services;
using Microsoft.Extensions.Logging;
namespace Webhooks.API.IntegrationEvents
{
public class OrderStatusChangedToShippedIntegrationEventHandler : IIntegrationEventHandler<OrderStatusChangedToShippedIntegrationEvent>
{
private readonly IWebhooksRetriever _retriever;
private readonly IWebhooksSender _sender;
private readonly ILogger _logger;
public OrderStatusChangedToShippedIntegrationEventHandler(IWebhooksRetriever retriever, IWebhooksSender sender, ILogger<OrderStatusChangedToShippedIntegrationEventHandler> logger )
{
_retriever = retriever;
_sender = sender;
_logger = logger;
}
public async Task Handle(OrderStatusChangedToShippedIntegrationEvent @event)
{
var subscriptions = await _retriever.GetSubscriptionsOfType(WebhookType.OrderShipped);
_logger.LogInformation($"Received OrderStatusChangedToShippedIntegrationEvent and got {subscriptions.Count()} subscriptions to process");
var whook = new WebhookData(WebhookType.OrderShipped, @event);
await _sender.SendAll(subscriptions, whook);
}
}
}

View File

@ -0,0 +1,24 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.IntegrationEvents
{
public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
public int ProductId { get; private set; }
public decimal NewPrice { get; private set; }
public decimal OldPrice { get; private set; }
public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice)
{
ProductId = productId;
NewPrice = newPrice;
OldPrice = oldPrice;
}
}
}

View File

@ -0,0 +1,16 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.IntegrationEvents
{
public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>
{
public async Task Handle(ProductPriceChangedIntegrationEvent @event)
{
int i = 0;
}
}
}

View File

@ -0,0 +1,47 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Webhooks.API.Infrastructure;
namespace Webhooks.API.Migrations
{
[DbContext(typeof(WebhooksContext))]
[Migration("20190118091148_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.1-servicing-10028")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Webhooks.API.Model.WebhookSubscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("Date");
b.Property<string>("DestUrl");
b.Property<string>("Token");
b.Property<int>("Type");
b.Property<string>("UserId");
b.HasKey("Id");
b.ToTable("Subscriptions");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Webhooks.API.Migrations
{
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Subscriptions",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Type = table.Column<int>(nullable: false),
Date = table.Column<DateTime>(nullable: false),
DestUrl = table.Column<string>(nullable: true),
Token = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Subscriptions", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Subscriptions");
}
}
}

View File

@ -0,0 +1,45 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Webhooks.API.Infrastructure;
namespace Webhooks.API.Migrations
{
[DbContext(typeof(WebhooksContext))]
partial class WebhooksContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.1-servicing-10028")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Webhooks.API.Model.WebhookSubscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("Date");
b.Property<string>("DestUrl");
b.Property<string>("Token");
b.Property<int>("Type");
b.Property<string>("UserId");
b.HasKey("Id");
b.ToTable("Subscriptions");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,26 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.Model
{
public class WebhookData
{
public DateTime When { get; }
public string Payload { get; }
public string Type { get; }
public WebhookData(WebhookType hookType, object data)
{
When = DateTime.UtcNow;
Type = hookType.ToString();
Payload = JsonConvert.SerializeObject(data);
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.Model
{
public class WebhookSubscription
{
public int Id { get; set; }
public WebhookType Type { get; set; }
public DateTime Date { get; set; }
public string DestUrl { get; set; }
public string Token { get; set; }
public string UserId { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.Model
{
public enum WebhookType
{
CatalogItemPriceChange = 1,
OrderShipped = 2,
OrderPaid = 3
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Webhooks.API.Infrastructure;
namespace Webhooks.API
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build()
.MigrateDbContext<WebhooksContext>((_,__) => { })
.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}

View File

@ -0,0 +1,32 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:62486",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Webhooks.API": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
}
}
}

View File

@ -0,0 +1,57 @@
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace Webhooks.API.Services
{
class GrantUrlTesterService : IGrantUrlTesterService
{
private readonly IHttpClientFactory _clientFactory;
private readonly ILogger _logger;
public GrantUrlTesterService(IHttpClientFactory factory, ILogger<IGrantUrlTesterService> logger)
{
_clientFactory = factory;
_logger = logger;
}
public async Task<bool> TestGrantUrl(string urlHook, string url, string token)
{
if (!CheckSameOrigin(urlHook, url))
{
_logger.LogWarning($"Url of the hook ({urlHook} and the grant url ({url} do not belong to same origin)");
return false;
}
var client = _clientFactory.CreateClient("GrantClient");
var msg = new HttpRequestMessage(HttpMethod.Options, url);
msg.Headers.Add("X-eshop-whtoken", token);
_logger.LogInformation($"Sending the OPTIONS message to {url} with token {token ?? string.Empty}");
try
{
var response = await client.SendAsync(msg);
var tokenReceived = response.Headers.TryGetValues("X-eshop-whtoken", out var tokenValues) ? tokenValues.FirstOrDefault() : null;
var tokenExpected = string.IsNullOrWhiteSpace(token) ? null : token;
_logger.LogInformation($"Response code is {response.StatusCode} for url {url} and token in header was {tokenReceived} (expected token was {tokenExpected})");
return response.IsSuccessStatusCode && tokenReceived == tokenExpected;
}
catch (Exception ex)
{
_logger.LogWarning($"Exception {ex.GetType().Name} when sending OPTIONS request. Url can't be granted.");
return false;
}
}
private bool CheckSameOrigin(string urlHook, string url)
{
var firstUrl = new Uri(urlHook, UriKind.Absolute);
var secondUrl = new Uri(url, UriKind.Absolute);
return firstUrl.Scheme == secondUrl.Scheme &&
firstUrl.Port == secondUrl.Port &&
firstUrl.Host == firstUrl.Host;
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Webhooks.API.Services
{
public interface IGrantUrlTesterService
{
Task<bool> TestGrantUrl(string urlHook, string url, string token);
}
}

View File

@ -0,0 +1,7 @@
namespace Webhooks.API.Services
{
public interface IIdentityService
{
string GetUserIdentity();
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Webhooks.API.Model;
namespace Webhooks.API.Services
{
public interface IWebhooksRetriever
{
Task<IEnumerable<WebhookSubscription>> GetSubscriptionsOfType(WebhookType type);
}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Webhooks.API.Model;
namespace Webhooks.API.Services
{
public interface IWebhooksSender
{
Task SendAll(IEnumerable<WebhookSubscription> receivers, WebhookData data);
}
}

View File

@ -0,0 +1,21 @@

using Microsoft.AspNetCore.Http;
using System;
namespace Webhooks.API.Services
{
public class IdentityService : IIdentityService
{
private IHttpContextAccessor _context;
public IdentityService(IHttpContextAccessor context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public string GetUserIdentity()
{
return _context.HttpContext.User.FindFirst("sub").Value;
}
}
}

View File

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Webhooks.API.Infrastructure;
using Webhooks.API.Model;
namespace Webhooks.API.Services
{
public class WebhooksRetriever : IWebhooksRetriever
{
private readonly WebhooksContext _db;
public WebhooksRetriever(WebhooksContext db)
{
_db = db;
}
public async Task<IEnumerable<WebhookSubscription>> GetSubscriptionsOfType(WebhookType type)
{
var data = await _db.Subscriptions.Where(s => s.Type == type).ToListAsync();
return data;
}
}
}

View File

@ -0,0 +1,49 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Webhooks.API.Model;
namespace Webhooks.API.Services
{
public class WebhooksSender : IWebhooksSender
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger;
public WebhooksSender(IHttpClientFactory httpClientFactory, ILogger<WebhooksSender> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}
public async Task SendAll(IEnumerable<WebhookSubscription> receivers, WebhookData data)
{
var client = _httpClientFactory.CreateClient();
var json = JsonConvert.SerializeObject(data);
var tasks = receivers.Select(r => OnSendData(r, json, client));
await Task.WhenAll(tasks.ToArray());
}
private Task OnSendData(WebhookSubscription subs, string jsonData, HttpClient client)
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(subs.DestUrl, UriKind.Absolute),
Method = HttpMethod.Post,
Content = new StringContent(jsonData, Encoding.UTF8, "application/json")
};
if (!string.IsNullOrWhiteSpace(subs.Token))
{
request.Headers.Add("X-eshop-whtoken", subs.Token);
}
_logger.LogDebug($"Sending hook to {subs.DestUrl} of type {subs.Type.ToString()}");
return client.SendAsync(request);
}
}
}

View File

@ -0,0 +1,372 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using HealthChecks.UI.Client;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.ServiceFabric;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.ServiceBus;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using Swashbuckle.AspNetCore.Swagger;
using Webhooks.API.Infrastructure;
using Webhooks.API.IntegrationEvents;
using Webhooks.API.Services;
namespace Webhooks.API
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddAppInsight(Configuration)
.AddCustomMVC(Configuration)
.AddCustomDbContext(Configuration)
.AddSwagger(Configuration)
.AddCustomHealthCheck(Configuration)
.AddHttpClientServices(Configuration)
.AddIntegrationServices(Configuration)
.AddEventBus(Configuration)
.AddCustomAuthentication(Configuration)
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>()
.AddTransient<IIdentityService, IdentityService>()
.AddTransient<IGrantUrlTesterService, GrantUrlTesterService>()
.AddTransient<IWebhooksRetriever, WebhooksRetriever>()
.AddTransient<IWebhooksSender, WebhooksSender>();
var container = new ContainerBuilder();
container.Populate(services);
return new AutofacServiceProvider(container.Build());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
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);
}
app.UseHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
app.UseCors("CorsPolicy");
ConfigureAuth(app);
app.UseMvcWithDefaultRoute();
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Webhooks.API V1");
c.OAuthClientId("webhooksswaggerui");
c.OAuthAppName("WebHooks Service Swagger UI");
});
ConfigureEventBus(app);
}
protected virtual void ConfigureAuth(IApplicationBuilder app)
{
/*
if (Configuration.GetValue<bool>("UseLoadTest"))
{
app.UseMiddleware<ByPassAuthMiddleware>();
}
*/
app.UseAuthentication();
}
protected virtual void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToShippedIntegrationEvent, OrderStatusChangedToShippedIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>();
}
}
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.AddMvc(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddControllersAsServices();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<WebhooksContext>(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);
});
// Changing default behavior when client evaluation occurs to throw.
// Default in EF Core would be to log a warning when client evaluation is performed.
options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
//Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval
});
return services;
}
public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration)
{
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "eShopOnContainers - Webhooks HTTP API",
Version = "v1",
Description = "The Webhooks Microservice HTTP API. This is a simple webhooks CRUD registration entrypoint",
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>()
{
{ "webhooks", "Webhooks API" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
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 =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
});
}
else
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
});
}
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddTransient<ProductPriceChangedIntegrationEventHandler>();
services.AddTransient<OrderStatusChangedToShippedIntegrationEventHandler>();
services.AddTransient<OrderStatusChangedToPaidIntegrationEventHandler>();
return services;
}
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
{
var accountName = configuration.GetValue<string>("AzureStorageAccountName");
var accountKey = configuration.GetValue<string>("AzureStorageAccountKey");
var hcBuilder = services.AddHealthChecks();
hcBuilder
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddSqlServer(
configuration["ConnectionString"],
name: "WebhooksApiDb-check",
tags: new string[] { "webhooksdb" });
return services;
}
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(Timeout.InfiniteTimeSpan);
//add http client services
services.AddHttpClient("GrantClient")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
//.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>();
return services;
}
public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>();
var serviceBusConnection = new ServiceBusConnectionStringBuilder(configuration["EventBusConnection"]);
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 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 = "webhooks";
});
return services;
}
}
}

View File

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.0.2105168" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" />
<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.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word"
}

View File

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"SubscriptionClientName": "Webhooks",
"EventBusRetryCount": 5
}

View File

@ -0,0 +1 @@
!wwwroot

1
src/Web/WebhookClient/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!wwwroot/lib

View File

@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace WebhookClient.Controllers
{
[Authorize]
public class AccountController : Controller
{
public async Task<IActionResult> SignIn(string returnUrl)
{
var user = User as ClaimsPrincipal;
var token = await HttpContext.GetTokenAsync("access_token");
if (token != null)
{
ViewData["access_token"] = token;
}
return RedirectToPage("/Index");
}
public async Task<IActionResult> Signout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
var homeUrl = Url.Page("/Index");
return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme,
new AuthenticationProperties { RedirectUri = homeUrl });
}
}
}

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