Merge branch 'features/webhooks' into dev
This commit is contained in:
		
						commit
						0eae3c3ea3
					
				@ -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
 | 
			
		||||
 | 
			
		||||
@ -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"
 | 
			
		||||
@ -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    
 | 
			
		||||
@ -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}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -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 }}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								k8s/helm/webhooks-api/.helmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								k8s/helm/webhooks-api/.helmignore
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										5
									
								
								k8s/helm/webhooks-api/Chart.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								k8s/helm/webhooks-api/Chart.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
appVersion: "1.0"
 | 
			
		||||
description: A Helm chart for Kubernetes
 | 
			
		||||
name: webhooks-api
 | 
			
		||||
version: 0.1.0
 | 
			
		||||
							
								
								
									
										8
									
								
								k8s/helm/webhooks-api/templates/NOTES.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								k8s/helm/webhooks-api/templates/NOTES.txt
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										32
									
								
								k8s/helm/webhooks-api/templates/_helpers.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								k8s/helm/webhooks-api/templates/_helpers.tpl
									
									
									
									
									
										Normal 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 -}}
 | 
			
		||||
							
								
								
									
										51
									
								
								k8s/helm/webhooks-api/templates/_names.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								k8s/helm/webhooks-api/templates/_names.tpl
									
									
									
									
									
										Normal 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 -}}
 | 
			
		||||
							
								
								
									
										20
									
								
								k8s/helm/webhooks-api/templates/configmap.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								k8s/helm/webhooks-api/templates/configmap.yaml
									
									
									
									
									
										Normal 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 }}"
 | 
			
		||||
							
								
								
									
										71
									
								
								k8s/helm/webhooks-api/templates/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								k8s/helm/webhooks-api/templates/deployment.yaml
									
									
									
									
									
										Normal 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 }}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								k8s/helm/webhooks-api/templates/ingress.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								k8s/helm/webhooks-api/templates/ingress.yaml
									
									
									
									
									
										Normal 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 }}
 | 
			
		||||
							
								
								
									
										19
									
								
								k8s/helm/webhooks-api/templates/service.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								k8s/helm/webhooks-api/templates/service.yaml
									
									
									
									
									
										Normal 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 }}
 | 
			
		||||
							
								
								
									
										53
									
								
								k8s/helm/webhooks-api/values.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								k8s/helm/webhooks-api/values.yaml
									
									
									
									
									
										Normal 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'
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								k8s/helm/webhooks-web/.helmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								k8s/helm/webhooks-web/.helmignore
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										5
									
								
								k8s/helm/webhooks-web/Chart.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								k8s/helm/webhooks-web/Chart.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
appVersion: "1.0"
 | 
			
		||||
description: A Helm chart for Kubernetes
 | 
			
		||||
name: webhooks-web
 | 
			
		||||
version: 0.1.0
 | 
			
		||||
							
								
								
									
										8
									
								
								k8s/helm/webhooks-web/templates/NOTES.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								k8s/helm/webhooks-web/templates/NOTES.txt
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										32
									
								
								k8s/helm/webhooks-web/templates/_helpers.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								k8s/helm/webhooks-web/templates/_helpers.tpl
									
									
									
									
									
										Normal 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 -}}
 | 
			
		||||
							
								
								
									
										51
									
								
								k8s/helm/webhooks-web/templates/_names.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								k8s/helm/webhooks-web/templates/_names.tpl
									
									
									
									
									
										Normal 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 -}}
 | 
			
		||||
							
								
								
									
										19
									
								
								k8s/helm/webhooks-web/templates/configmap.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								k8s/helm/webhooks-web/templates/configmap.yaml
									
									
									
									
									
										Normal 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 }}
 | 
			
		||||
							
								
								
									
										71
									
								
								k8s/helm/webhooks-web/templates/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								k8s/helm/webhooks-web/templates/deployment.yaml
									
									
									
									
									
										Normal 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 }}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								k8s/helm/webhooks-web/templates/ingress.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								k8s/helm/webhooks-web/templates/ingress.yaml
									
									
									
									
									
										Normal 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 }}
 | 
			
		||||
							
								
								
									
										19
									
								
								k8s/helm/webhooks-web/templates/service.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								k8s/helm/webhooks-web/templates/service.yaml
									
									
									
									
									
										Normal 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 }}
 | 
			
		||||
							
								
								
									
										52
									
								
								k8s/helm/webhooks-web/values.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								k8s/helm/webhooks-web/values.yaml
									
									
									
									
									
										Normal 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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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())
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -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");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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) });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/Services/Webhooks/Webhooks.API/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Services/Webhooks/Webhooks.API/Dockerfile
									
									
									
									
									
										Normal 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"]
 | 
			
		||||
@ -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
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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" } }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								src/Services/Webhooks/Webhooks.API/Migrations/20190118091148_Initial.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/Services/Webhooks/Webhooks.API/Migrations/20190118091148_Initial.Designer.cs
									
									
									
										generated
									
									
									
										Normal 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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/Services/Webhooks/Webhooks.API/Model/WebhookData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/Services/Webhooks/Webhooks.API/Model/WebhookData.cs
									
									
									
									
									
										Normal 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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/Services/Webhooks/Webhooks.API/Model/WebhookType.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Services/Webhooks/Webhooks.API/Model/WebhookType.cs
									
									
									
									
									
										Normal 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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/Services/Webhooks/Webhooks.API/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/Services/Webhooks/Webhooks.API/Program.cs
									
									
									
									
									
										Normal 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>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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}"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
namespace Webhooks.API.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface IIdentityService
 | 
			
		||||
    {
 | 
			
		||||
        string GetUserIdentity();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										372
									
								
								src/Services/Webhooks/Webhooks.API/Startup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										372
									
								
								src/Services/Webhooks/Webhooks.API/Startup.cs
									
									
									
									
									
										Normal 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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj
									
									
									
									
									
										Normal 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>
 | 
			
		||||
@ -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"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								src/Services/Webhooks/Webhooks.API/appsettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Services/Webhooks/Webhooks.API/appsettings.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
{
 | 
			
		||||
  "Logging": {
 | 
			
		||||
    "LogLevel": {
 | 
			
		||||
      "Default": "Warning"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "AllowedHosts": "*",
 | 
			
		||||
  "SubscriptionClientName": "Webhooks",
 | 
			
		||||
  "EventBusRetryCount": 5
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								src/Web/WebhookClient/.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/Web/WebhookClient/.dockerignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
!wwwroot
 | 
			
		||||
							
								
								
									
										1
									
								
								src/Web/WebhookClient/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/Web/WebhookClient/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
!wwwroot/lib
 | 
			
		||||
							
								
								
									
										40
									
								
								src/Web/WebhookClient/Controllers/AccountController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/Web/WebhookClient/Controllers/AccountController.cs
									
									
									
									
									
										Normal 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 });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,56 @@
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
using WebhookClient.Services;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Controllers
 | 
			
		||||
{
 | 
			
		||||
    [ApiController]
 | 
			
		||||
    [Route("webhook-received")]
 | 
			
		||||
    public class WebhooksReceivedController : Controller
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        private readonly Settings _settings;
 | 
			
		||||
        private readonly ILogger _logger;
 | 
			
		||||
        private readonly IHooksRepository _hooksRepository;
 | 
			
		||||
        
 | 
			
		||||
        public WebhooksReceivedController(IOptions<Settings> settings, ILogger<WebhooksReceivedController> logger, IHooksRepository hooksRepository)
 | 
			
		||||
        {
 | 
			
		||||
            _settings = settings.Value;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _hooksRepository = hooksRepository;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [HttpPost]
 | 
			
		||||
        public async Task<IActionResult> NewWebhook(WebhookData hook)
 | 
			
		||||
        {
 | 
			
		||||
            var header = Request.Headers[HeaderNames.WebHookCheckHeader];
 | 
			
		||||
            var token = header.FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
            _logger.LogInformation($"Received hook with token {token}. My token is {_settings.Token}. Token validation is set to {_settings.ValidateToken}");
 | 
			
		||||
 | 
			
		||||
            if (!_settings.ValidateToken || _settings.Token == token)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogInformation($"Received hook is going to be processed");
 | 
			
		||||
                var newHook = new WebHookReceived()
 | 
			
		||||
                {
 | 
			
		||||
                    Data = hook.Payload,
 | 
			
		||||
                    When = hook.When,
 | 
			
		||||
                    Token = token
 | 
			
		||||
                };
 | 
			
		||||
                await _hooksRepository.AddNew(newHook);
 | 
			
		||||
                _logger.LogInformation($"Received hook was processed.");
 | 
			
		||||
                return Ok(newHook);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _logger.LogInformation($"Received hook is NOT processed - Bad Request returned.");
 | 
			
		||||
            return BadRequest();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/Web/WebhookClient/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/Web/WebhookClient/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
EXPOSE 80
 | 
			
		||||
EXPOSE 443
 | 
			
		||||
 | 
			
		||||
FROM microsoft/dotnet:2.2-sdk AS build
 | 
			
		||||
WORKDIR /src
 | 
			
		||||
COPY ["src/Web/WebhookClient/WebhookClient.csproj", "src/Web/WebhookClient/"]
 | 
			
		||||
RUN dotnet restore "src/Web/WebhookClient/WebhookClient.csproj"
 | 
			
		||||
COPY . .
 | 
			
		||||
WORKDIR "/src/src/Web/WebhookClient"
 | 
			
		||||
RUN dotnet build "WebhookClient.csproj" -c Release -o /app
 | 
			
		||||
 | 
			
		||||
FROM build AS publish
 | 
			
		||||
RUN dotnet publish "WebhookClient.csproj" -c Release -o /app
 | 
			
		||||
 | 
			
		||||
FROM base AS final
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
COPY --from=publish /app .
 | 
			
		||||
ENTRYPOINT ["dotnet", "WebhookClient.dll"]
 | 
			
		||||
							
								
								
									
										12
									
								
								src/Web/WebhookClient/HeaderNames.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Web/WebhookClient/HeaderNames.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient
 | 
			
		||||
{
 | 
			
		||||
    static class HeaderNames
 | 
			
		||||
    {
 | 
			
		||||
        public const string WebHookCheckHeader = "X-eshop-whtoken";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,51 @@
 | 
			
		||||
using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Net.Http.Headers;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient
 | 
			
		||||
{
 | 
			
		||||
    public class HttpClientAuthorizationDelegatingHandler
 | 
			
		||||
            : DelegatingHandler
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IHttpContextAccessor _httpContextAccesor;
 | 
			
		||||
 | 
			
		||||
        public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor)
 | 
			
		||||
        {
 | 
			
		||||
            _httpContextAccesor = httpContextAccesor;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            var authorizationHeader = _httpContextAccesor.HttpContext
 | 
			
		||||
                .Request.Headers["Authorization"];
 | 
			
		||||
 | 
			
		||||
            if (!string.IsNullOrEmpty(authorizationHeader))
 | 
			
		||||
            {
 | 
			
		||||
                request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var token = await GetToken();
 | 
			
		||||
 | 
			
		||||
            if (token != null)
 | 
			
		||||
            {
 | 
			
		||||
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return await base.SendAsync(request, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async Task<string> GetToken()
 | 
			
		||||
        {
 | 
			
		||||
            const string ACCESS_TOKEN = "access_token";
 | 
			
		||||
 | 
			
		||||
            return await _httpContextAccesor.HttpContext
 | 
			
		||||
                .GetTokenAsync(ACCESS_TOKEN);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/Web/WebhookClient/Models/WebHookReceived.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Web/WebhookClient/Models/WebHookReceived.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Models
 | 
			
		||||
{
 | 
			
		||||
    public class WebHookReceived
 | 
			
		||||
    {
 | 
			
		||||
        public DateTime When { get; set; }
 | 
			
		||||
        public string Data { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Token { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/Web/WebhookClient/Models/WebhookData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Web/WebhookClient/Models/WebhookData.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Models
 | 
			
		||||
{
 | 
			
		||||
    public class WebhookData
 | 
			
		||||
    {
 | 
			
		||||
        public DateTime When { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Payload { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Type { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/Web/WebhookClient/Models/WebhookResponse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Web/WebhookClient/Models/WebhookResponse.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Models
 | 
			
		||||
{
 | 
			
		||||
    public class WebhookResponse
 | 
			
		||||
    {
 | 
			
		||||
        public DateTime Date { get; set; }
 | 
			
		||||
        public string DestUrl { get; set; }
 | 
			
		||||
        public string Token { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								src/Web/WebhookClient/Models/WebhookSubscriptionRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Web/WebhookClient/Models/WebhookSubscriptionRequest.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Models
 | 
			
		||||
{
 | 
			
		||||
    public class WebhookSubscriptionRequest
 | 
			
		||||
    {
 | 
			
		||||
        public string Url { get; set; }
 | 
			
		||||
        public string Token { get; set; }
 | 
			
		||||
        public string Event { get; set; }
 | 
			
		||||
        public string GrantUrl { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/Web/WebhookClient/Pages/Error.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/Web/WebhookClient/Pages/Error.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
@page
 | 
			
		||||
@model ErrorModel
 | 
			
		||||
@{
 | 
			
		||||
    ViewData["Title"] = "Error";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
<h1 class="text-danger">Error.</h1>
 | 
			
		||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
 | 
			
		||||
 | 
			
		||||
@if (Model.ShowRequestId)
 | 
			
		||||
{
 | 
			
		||||
    <p>
 | 
			
		||||
        <strong>Request ID:</strong> <code>@Model.RequestId</code>
 | 
			
		||||
    </p>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
<h3>Development Mode</h3>
 | 
			
		||||
<p>
 | 
			
		||||
    Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
 | 
			
		||||
</p>
 | 
			
		||||
<p>
 | 
			
		||||
    <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
 | 
			
		||||
    It can result in displaying sensitive information from exceptions to end users.
 | 
			
		||||
    For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
 | 
			
		||||
    and restarting the app.
 | 
			
		||||
</p>
 | 
			
		||||
							
								
								
									
										23
									
								
								src/Web/WebhookClient/Pages/Error.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/Web/WebhookClient/Pages/Error.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Pages
 | 
			
		||||
{
 | 
			
		||||
    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
 | 
			
		||||
    public class ErrorModel : PageModel
 | 
			
		||||
    {
 | 
			
		||||
        public string RequestId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
 | 
			
		||||
 | 
			
		||||
        public void OnGet()
 | 
			
		||||
        {
 | 
			
		||||
            RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								src/Web/WebhookClient/Pages/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/Web/WebhookClient/Pages/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
@page
 | 
			
		||||
@model IndexModel
 | 
			
		||||
@{
 | 
			
		||||
    ViewData["Title"] = "Home page";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
<div class="text-center">
 | 
			
		||||
    <h1 class="display-4">Welcome</h1>
 | 
			
		||||
    <p>eShopOnContainers - Webhook client</p>
 | 
			
		||||
    @if (!User.Identity.IsAuthenticated)
 | 
			
		||||
    {
 | 
			
		||||
        <a class="btn-primary btn" href="@Url.Action("SignIn", "Account")">Login</a>
 | 
			
		||||
        <p>Why I need to login? You only need to login <bold>to setup a new webhook</bold>.</p>
 | 
			
		||||
    }
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="table">
 | 
			
		||||
    <h3>Current webhooks received</h3>
 | 
			
		||||
    <p>(Data since last time web started up)<p>
 | 
			
		||||
    <table class="table">
 | 
			
		||||
        @foreach (var webhook in Model.WebHooksReceived)
 | 
			
		||||
        {
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>@webhook.When</td>
 | 
			
		||||
                <td><pre>@webhook.Data</pre></td>
 | 
			
		||||
                <td>@(webhook.Token ?? "--None--")</td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        }
 | 
			
		||||
    </table>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								src/Web/WebhookClient/Pages/Index.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Web/WebhookClient/Pages/Index.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
using WebhookClient.Services;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Pages
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public class IndexModel : PageModel
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IHooksRepository _hooksRepository;
 | 
			
		||||
 | 
			
		||||
        public IndexModel(IHooksRepository hooksRepository)
 | 
			
		||||
        {
 | 
			
		||||
            _hooksRepository = hooksRepository;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<WebHookReceived> WebHooksReceived { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public async Task OnGet()
 | 
			
		||||
        {
 | 
			
		||||
            WebHooksReceived = await _hooksRepository.GetAll();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/Web/WebhookClient/Pages/Privacy.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Web/WebhookClient/Pages/Privacy.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
@page
 | 
			
		||||
@model PrivacyModel
 | 
			
		||||
@{
 | 
			
		||||
    ViewData["Title"] = "Privacy Policy";
 | 
			
		||||
}
 | 
			
		||||
<h1>@ViewData["Title"]</h1>
 | 
			
		||||
 | 
			
		||||
<p>Use this page to detail your site's privacy policy.</p>
 | 
			
		||||
							
								
								
									
										16
									
								
								src/Web/WebhookClient/Pages/Privacy.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Web/WebhookClient/Pages/Privacy.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Pages
 | 
			
		||||
{
 | 
			
		||||
    public class PrivacyModel : PageModel
 | 
			
		||||
    {
 | 
			
		||||
        public void OnGet()
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/Web/WebhookClient/Pages/RegisterWebhook.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Web/WebhookClient/Pages/RegisterWebhook.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
@page
 | 
			
		||||
@model WebhookClient.Pages.RegisterWebhookModel
 | 
			
		||||
@{
 | 
			
		||||
    ViewData["Title"] = "RegisterWebhook";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
<h3>Register webhook</h3>
 | 
			
		||||
 | 
			
		||||
<p>This page registers the "OrderPaid" Webhook by sending a POST to webhooks.</p>
 | 
			
		||||
 | 
			
		||||
<form method="post">
 | 
			
		||||
    <p>Token: <input type="text" asp-for="Token" /></p>
 | 
			
		||||
    <input type="submit" value="send" />
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
@if (Model.ResponseCode != (int)System.Net.HttpStatusCode.OK)
 | 
			
		||||
{
 | 
			
		||||
    <p>Error @Model.ResponseCode (@Model.ResponseMessage) when calling the Webhooks API (@Model.RequestUrl) with GrantUrl: @Model.GrantUrl):(</p>
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										81
									
								
								src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Net.Http.Formatting;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Pages
 | 
			
		||||
{
 | 
			
		||||
    [Authorize]
 | 
			
		||||
 | 
			
		||||
    public class RegisterWebhookModel : PageModel
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Settings _settings;
 | 
			
		||||
        private readonly IHttpClientFactory _httpClientFactory;
 | 
			
		||||
 | 
			
		||||
        [BindProperty] public string Token { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int ResponseCode { get; set; }
 | 
			
		||||
        public string RequestUrl { get; set; }
 | 
			
		||||
        public string GrantUrl { get; set; }
 | 
			
		||||
        public string ResponseMessage { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public RegisterWebhookModel(IOptions<Settings> settings, IHttpClientFactory httpClientFactory)
 | 
			
		||||
        {
 | 
			
		||||
            _settings = settings.Value;
 | 
			
		||||
            _httpClientFactory = httpClientFactory;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void OnGet()
 | 
			
		||||
        {
 | 
			
		||||
            ResponseCode = (int)HttpStatusCode.OK;
 | 
			
		||||
            Token = _settings.Token;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<IActionResult> OnPost()
 | 
			
		||||
        {
 | 
			
		||||
            ResponseCode = (int)HttpStatusCode.OK;
 | 
			
		||||
            var protocol = Request.IsHttps ? "https" : "http";
 | 
			
		||||
            var selfurl = !string.IsNullOrEmpty(_settings.SelfUrl) ? _settings.SelfUrl : $"{protocol}://{Request.Host}/{Request.PathBase}";
 | 
			
		||||
            if (!selfurl.EndsWith("/"))
 | 
			
		||||
            {
 | 
			
		||||
                selfurl = selfurl + "/";
 | 
			
		||||
            }
 | 
			
		||||
            var granturl = $"{selfurl}check";
 | 
			
		||||
            var url = $"{selfurl}webhook-received";
 | 
			
		||||
            var client = _httpClientFactory.CreateClient("GrantClient");
 | 
			
		||||
 | 
			
		||||
            var payload = new WebhookSubscriptionRequest()
 | 
			
		||||
            {
 | 
			
		||||
                Event = "OrderPaid",
 | 
			
		||||
                GrantUrl = granturl,
 | 
			
		||||
                Url = url,
 | 
			
		||||
                Token = Token
 | 
			
		||||
            };
 | 
			
		||||
            var response = await client.PostAsync<WebhookSubscriptionRequest>(_settings.WebhooksUrl + "/api/v1/webhooks", payload, new JsonMediaTypeFormatter());
 | 
			
		||||
 | 
			
		||||
            if (response.IsSuccessStatusCode)
 | 
			
		||||
            {
 | 
			
		||||
                return RedirectToPage("WebhooksList");
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                ResponseCode = (int)response.StatusCode;
 | 
			
		||||
                ResponseMessage = response.ReasonPhrase;
 | 
			
		||||
                GrantUrl = granturl;
 | 
			
		||||
                RequestUrl = $"{response.RequestMessage.Method} {response.RequestMessage.RequestUri}";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Page();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								src/Web/WebhookClient/Pages/Shared/_Layout.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/Web/WebhookClient/Pages/Shared/_Layout.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>@ViewData["Title"] - WebhookClient</title>
 | 
			
		||||
 | 
			
		||||
    <environment include="Development">
 | 
			
		||||
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
 | 
			
		||||
    </environment>
 | 
			
		||||
    <environment exclude="Development">
 | 
			
		||||
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css"
 | 
			
		||||
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
 | 
			
		||||
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
 | 
			
		||||
              crossorigin="anonymous"
 | 
			
		||||
              integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE="/>
 | 
			
		||||
    </environment>
 | 
			
		||||
    <link rel="stylesheet" href="~/css/site.css" />
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <header>
 | 
			
		||||
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
 | 
			
		||||
            <div class="container">
 | 
			
		||||
                <a class="navbar-brand" asp-area="" asp-page="/Index">WebhookClient</a>
 | 
			
		||||
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
 | 
			
		||||
                        aria-expanded="false" aria-label="Toggle navigation">
 | 
			
		||||
                    <span class="navbar-toggler-icon"></span>
 | 
			
		||||
                </button>
 | 
			
		||||
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
 | 
			
		||||
                    <ul class="navbar-nav flex-grow-1">
 | 
			
		||||
                        <li class="nav-item">
 | 
			
		||||
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li class="nav-item">
 | 
			
		||||
                            <a class="nav-link text-dark" asp-area="" asp-page="/RegisterWebhook">Register webhook</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li class="nav-item">
 | 
			
		||||
                            <a class="nav-link text-dark" asp-area="" asp-page="/WebhooksList">Webhooks registered (in API)</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                    </ul>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </nav>
 | 
			
		||||
    </header>
 | 
			
		||||
    <div class="container">
 | 
			
		||||
        <main role="main" class="pb-3">
 | 
			
		||||
            @RenderBody()
 | 
			
		||||
        </main>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <footer class="border-top footer text-muted">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            © 2019 - WebhookClient - <a asp-area="" asp-page="/RegisterWebhook">Register Webhook</a> | <a asp-area="" asp-page="/WebhooksList">Webhooks registered in API</a>
 | 
			
		||||
        </div>
 | 
			
		||||
    </footer>
 | 
			
		||||
 | 
			
		||||
    <environment include="Development">
 | 
			
		||||
        <script src="~/lib/jquery/dist/jquery.js"></script>
 | 
			
		||||
        <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
 | 
			
		||||
    </environment>
 | 
			
		||||
    <environment exclude="Development">
 | 
			
		||||
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
 | 
			
		||||
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
 | 
			
		||||
                asp-fallback-test="window.jQuery"
 | 
			
		||||
                crossorigin="anonymous"
 | 
			
		||||
                integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
 | 
			
		||||
        </script>
 | 
			
		||||
        <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.min.js"
 | 
			
		||||
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"
 | 
			
		||||
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
 | 
			
		||||
                crossorigin="anonymous"
 | 
			
		||||
                integrity="sha256-E/V4cWE4qvAeO5MOhjtGtqDzPndRO1LBk8lJ/PR7CA4=">
 | 
			
		||||
        </script>
 | 
			
		||||
    </environment>
 | 
			
		||||
    <script src="~/js/site.js" asp-append-version="true"></script>
 | 
			
		||||
 | 
			
		||||
    @RenderSection("Scripts", required: false)
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
<environment include="Development">
 | 
			
		||||
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
 | 
			
		||||
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
 | 
			
		||||
</environment>
 | 
			
		||||
<environment exclude="Development">
 | 
			
		||||
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"
 | 
			
		||||
            asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
 | 
			
		||||
            asp-fallback-test="window.jQuery && window.jQuery.validator"
 | 
			
		||||
            crossorigin="anonymous"
 | 
			
		||||
            integrity="sha256-F6h55Qw6sweK+t7SiOJX+2bpSAa3b/fnlrVCJvmEj1A=">
 | 
			
		||||
    </script>
 | 
			
		||||
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"
 | 
			
		||||
            asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
 | 
			
		||||
            asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
 | 
			
		||||
            crossorigin="anonymous"
 | 
			
		||||
            integrity="sha256-9GycpJnliUjJDVDqP0UEu/bsm9U+3dnQUH8+3W10vkY=">
 | 
			
		||||
    </script>
 | 
			
		||||
</environment>
 | 
			
		||||
							
								
								
									
										28
									
								
								src/Web/WebhookClient/Pages/WebhooksList.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Web/WebhookClient/Pages/WebhooksList.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
@page
 | 
			
		||||
@model WebhookClient.Pages.WebhooksListModel
 | 
			
		||||
@{
 | 
			
		||||
    ViewData["Title"] = "WebhooksList";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
<h1>List of Webhooks registered by user @User.Identity.Name</h1>
 | 
			
		||||
 | 
			
		||||
<table class="table">
 | 
			
		||||
    <thead class="thead-light">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <th scope="col">Date</th>
 | 
			
		||||
            <th scope="col">Destination Url</th>
 | 
			
		||||
            <th scope="col">Validation token</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
 | 
			
		||||
    @foreach (var whr in Model.Webhooks)
 | 
			
		||||
    {
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td>@whr.Date</td>
 | 
			
		||||
            <td>@whr.DestUrl</td>
 | 
			
		||||
            <td>@whr.Token</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								src/Web/WebhookClient/Pages/WebhooksList.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Web/WebhookClient/Pages/WebhooksList.cshtml.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc.RazorPages;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
using WebhookClient.Services;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Pages
 | 
			
		||||
{
 | 
			
		||||
    public class WebhooksListModel : PageModel
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IWebhooksClient _webhooksClient;
 | 
			
		||||
 | 
			
		||||
        public IEnumerable<WebhookResponse> Webhooks { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public WebhooksListModel(IWebhooksClient webhooksClient)
 | 
			
		||||
        {
 | 
			
		||||
            _webhooksClient = webhooksClient;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task OnGet()
 | 
			
		||||
        {
 | 
			
		||||
            Webhooks = await _webhooksClient.LoadWebhooks();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/Web/WebhookClient/Pages/_ViewImports.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/Web/WebhookClient/Pages/_ViewImports.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
@using WebhookClient
 | 
			
		||||
@namespace WebhookClient.Pages
 | 
			
		||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
 | 
			
		||||
							
								
								
									
										3
									
								
								src/Web/WebhookClient/Pages/_ViewStart.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/Web/WebhookClient/Pages/_ViewStart.cshtml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
@{
 | 
			
		||||
    Layout = "_Layout";
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								src/Web/WebhookClient/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/Web/WebhookClient/Program.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient
 | 
			
		||||
{
 | 
			
		||||
    public class Program
 | 
			
		||||
    {
 | 
			
		||||
        public static void Main(string[] args)
 | 
			
		||||
        {
 | 
			
		||||
            CreateWebHostBuilder(args).Build().Run();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
 | 
			
		||||
            WebHost.CreateDefaultBuilder(args)
 | 
			
		||||
                .UseStartup<Startup>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								src/Web/WebhookClient/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/Web/WebhookClient/Properties/launchSettings.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
{
 | 
			
		||||
  "iisSettings": {
 | 
			
		||||
    "windowsAuthentication": false,
 | 
			
		||||
    "anonymousAuthentication": true,
 | 
			
		||||
    "iisExpress": {
 | 
			
		||||
      "applicationUrl": "http://localhost:51921",
 | 
			
		||||
      "sslPort": 44398
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "profiles": {
 | 
			
		||||
    "IIS Express": {
 | 
			
		||||
      "commandName": "IISExpress",
 | 
			
		||||
      "launchBrowser": true,
 | 
			
		||||
      "environmentVariables": {
 | 
			
		||||
        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "WebhookClient": {
 | 
			
		||||
      "commandName": "Project",
 | 
			
		||||
      "launchBrowser": true,
 | 
			
		||||
      "environmentVariables": {
 | 
			
		||||
        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
			
		||||
      },
 | 
			
		||||
      "applicationUrl": "https://localhost:5001;http://localhost:5000"
 | 
			
		||||
    },
 | 
			
		||||
    "Docker": {
 | 
			
		||||
      "commandName": "Docker",
 | 
			
		||||
      "launchBrowser": true,
 | 
			
		||||
      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/Web/WebhookClient/Services/IHooksRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Web/WebhookClient/Services/IHooksRepository.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface IHooksRepository
 | 
			
		||||
    {
 | 
			
		||||
        Task<IEnumerable<WebHookReceived>> GetAll();
 | 
			
		||||
        Task AddNew(WebHookReceived hook);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/Web/WebhookClient/Services/IWebhooksClient.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/Web/WebhookClient/Services/IWebhooksClient.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface IWebhooksClient
 | 
			
		||||
    {
 | 
			
		||||
        Task<IEnumerable<WebhookResponse>> LoadWebhooks();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/Web/WebhookClient/Services/InMemoryHooksRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/Web/WebhookClient/Services/InMemoryHooksRepository.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Services
 | 
			
		||||
{
 | 
			
		||||
    public class InMemoryHooksRepository : IHooksRepository
 | 
			
		||||
    {
 | 
			
		||||
        private readonly List<WebHookReceived> _data;
 | 
			
		||||
 | 
			
		||||
        public InMemoryHooksRepository() => _data = new List<WebHookReceived>();
 | 
			
		||||
 | 
			
		||||
        public Task AddNew(WebHookReceived hook)
 | 
			
		||||
        {
 | 
			
		||||
            _data.Add(hook);
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task<IEnumerable<WebHookReceived>> GetAll()
 | 
			
		||||
        {
 | 
			
		||||
            return Task.FromResult(_data.AsEnumerable());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								src/Web/WebhookClient/Services/WebhooksClient.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Web/WebhookClient/Services/WebhooksClient.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using WebhookClient.Models;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient.Services
 | 
			
		||||
{
 | 
			
		||||
    public class WebhooksClient : IWebhooksClient
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        private readonly IHttpClientFactory _httpClientFactory;
 | 
			
		||||
        private readonly Settings _settings;
 | 
			
		||||
        public WebhooksClient(IHttpClientFactory httpClientFactory, IOptions<Settings> settings)
 | 
			
		||||
        {
 | 
			
		||||
            _httpClientFactory = httpClientFactory;
 | 
			
		||||
            _settings = settings.Value;
 | 
			
		||||
        }
 | 
			
		||||
        public async Task<IEnumerable<WebhookResponse>> LoadWebhooks()
 | 
			
		||||
        {
 | 
			
		||||
            var client = _httpClientFactory.CreateClient("GrantClient");
 | 
			
		||||
            var response = await client.GetAsync(_settings.WebhooksUrl + "/api/v1/webhooks");
 | 
			
		||||
            var json = await response.Content.ReadAsStringAsync();
 | 
			
		||||
            var subscriptions = JsonConvert.DeserializeObject<IEnumerable<WebhookResponse>>(json);
 | 
			
		||||
            return subscriptions;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/Web/WebhookClient/Settings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Web/WebhookClient/Settings.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient
 | 
			
		||||
{
 | 
			
		||||
    public class Settings
 | 
			
		||||
    {
 | 
			
		||||
        public string Token { get; set; }
 | 
			
		||||
        public string IdentityUrl { get; set; }
 | 
			
		||||
        public string CallBackUrl { get; set; }
 | 
			
		||||
        public string WebhooksUrl { get; set; }
 | 
			
		||||
        public string SelfUrl { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool ValidateToken { get; set; }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										151
									
								
								src/Web/WebhookClient/Startup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/Web/WebhookClient/Startup.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,151 @@
 | 
			
		||||
using Microsoft.AspNetCore.Authentication.Cookies;
 | 
			
		||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.Extensions.Configuration;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using WebhookClient.Services;
 | 
			
		||||
 | 
			
		||||
namespace WebhookClient
 | 
			
		||||
{
 | 
			
		||||
    public class Startup
 | 
			
		||||
    {
 | 
			
		||||
        public Startup(IConfiguration configuration)
 | 
			
		||||
        {
 | 
			
		||||
            Configuration = configuration;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IConfiguration Configuration { get; }
 | 
			
		||||
 | 
			
		||||
        // This method gets called by the runtime. Use this method to add services to the container.
 | 
			
		||||
        public void ConfigureServices(IServiceCollection services)
 | 
			
		||||
        {
 | 
			
		||||
            services
 | 
			
		||||
                .AddSession(opt =>
 | 
			
		||||
                {
 | 
			
		||||
                    opt.Cookie.Name = ".eShopWebhooks.Session";
 | 
			
		||||
                })
 | 
			
		||||
                .AddConfiguration(Configuration)
 | 
			
		||||
                .AddHttpClientServices(Configuration)
 | 
			
		||||
                .AddCustomAuthentication(Configuration)
 | 
			
		||||
                .AddTransient<IWebhooksClient, WebhooksClient>()
 | 
			
		||||
                .AddSingleton<IHooksRepository, InMemoryHooksRepository>()
 | 
			
		||||
                .AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
 | 
			
		||||
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            var pathBase = Configuration["PATH_BASE"];
 | 
			
		||||
            if (!string.IsNullOrEmpty(pathBase))
 | 
			
		||||
            {
 | 
			
		||||
                app.UsePathBase(pathBase);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (env.IsDevelopment())
 | 
			
		||||
            {
 | 
			
		||||
                app.UseDeveloperExceptionPage();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                app.UseExceptionHandler("/Error");
 | 
			
		||||
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
 | 
			
		||||
            }
 | 
			
		||||
            app.UseAuthentication();
 | 
			
		||||
            app.Map("/check", capp =>
 | 
			
		||||
            {
 | 
			
		||||
                capp.Run(async (context) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if ("OPTIONS".Equals(context.Request.Method, StringComparison.InvariantCultureIgnoreCase))
 | 
			
		||||
                    {
 | 
			
		||||
                        var validateToken = bool.TrueString.Equals(Configuration["ValidateToken"], StringComparison.InvariantCultureIgnoreCase);
 | 
			
		||||
                        var header = context.Request.Headers[HeaderNames.WebHookCheckHeader];
 | 
			
		||||
                        var value = header.FirstOrDefault();
 | 
			
		||||
                        var tokenToValidate = Configuration["Token"];
 | 
			
		||||
                        if (!validateToken ||  value == tokenToValidate)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (!string.IsNullOrWhiteSpace(tokenToValidate))
 | 
			
		||||
                            {
 | 
			
		||||
                                context.Response.Headers.Add(HeaderNames.WebHookCheckHeader, tokenToValidate);
 | 
			
		||||
                            }
 | 
			
		||||
                            context.Response.StatusCode = (int)HttpStatusCode.OK;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            await context.Response.WriteAsync("Invalid token");
 | 
			
		||||
                            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            app.UseStaticFiles();
 | 
			
		||||
            app.UseSession();
 | 
			
		||||
            app.UseMvcWithDefaultRoute();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class ServiceExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static IServiceCollection AddConfiguration(this IServiceCollection services, IConfiguration configuration)
 | 
			
		||||
        {
 | 
			
		||||
            services.AddOptions();
 | 
			
		||||
            services.Configure<Settings>(configuration);
 | 
			
		||||
            return services;
 | 
			
		||||
        }
 | 
			
		||||
        public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
 | 
			
		||||
        {
 | 
			
		||||
            var identityUrl = configuration.GetValue<string>("IdentityUrl");
 | 
			
		||||
            var callBackUrl = configuration.GetValue<string>("CallBackUrl");
 | 
			
		||||
 | 
			
		||||
            // Add Authentication services          
 | 
			
		||||
 | 
			
		||||
            services.AddAuthentication(options =>
 | 
			
		||||
            {
 | 
			
		||||
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
 | 
			
		||||
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
 | 
			
		||||
            })
 | 
			
		||||
            .AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromHours(2))
 | 
			
		||||
            .AddOpenIdConnect(options =>
 | 
			
		||||
            {
 | 
			
		||||
                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
 | 
			
		||||
                options.Authority = identityUrl.ToString();
 | 
			
		||||
                options.SignedOutRedirectUri = callBackUrl.ToString();
 | 
			
		||||
                options.ClientId = "webhooksclient";
 | 
			
		||||
                options.ClientSecret = "secret";
 | 
			
		||||
                options.ResponseType = "code id_token";
 | 
			
		||||
                options.SaveTokens = true;
 | 
			
		||||
                options.GetClaimsFromUserInfoEndpoint = true;
 | 
			
		||||
                options.RequireHttpsMetadata = false;
 | 
			
		||||
                options.Scope.Add("openid");
 | 
			
		||||
                options.Scope.Add("webhooks");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return services;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration)
 | 
			
		||||
        {
 | 
			
		||||
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 | 
			
		||||
            services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
 | 
			
		||||
            services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(Timeout.InfiniteTimeSpan);
 | 
			
		||||
 | 
			
		||||
            //add http client services
 | 
			
		||||
            services.AddHttpClient("GrantClient")
 | 
			
		||||
                   .SetHandlerLifetime(TimeSpan.FromMinutes(5))
 | 
			
		||||
                   .AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>();
 | 
			
		||||
 | 
			
		||||
            return services;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user