From f9934638560de0f4dc9028b17f0c827a084492ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Fri, 30 Jun 2017 08:59:26 +0200 Subject: [PATCH 1/4] Implemented service bus for marketing.api locations.api graceperiodProcess payment.api --- .env | 12 +- deploy/az/servicebus/sbusdeploy.json | 84 +++++++++ docker-compose.dcproj | 8 +- docker-compose.override.yml | 27 +-- docker-compose.vs.debug.yml | 166 ------------------ docker-compose.vs.release.yml | 112 ------------ eShopOnContainers-ServicesAndWebApps.sln | 59 ++++++- src/Services/Basket/Basket.API/Startup.cs | 11 +- .../Basket/Basket.API/appsettings.json | 2 +- .../Catalog/Catalog.API/settings.json | 2 +- .../GracePeriodManager.csproj | 1 + .../GracePeriod/GracePeriodManager/Program.cs | 48 ++++- .../GracePeriodManager/appsettings.json | 4 +- .../Identity.API/Configuration/Config.cs | 15 ++ .../Controllers/LocationsController.cs | 2 +- .../Filters/AuthorizeCheckOperationFilter.cs | 31 ++++ .../Services/LocationsService.cs | 12 +- .../UserLocationUpdatedIntegrationEvent.cs | 8 +- .../Locations.API/Locations.API.csproj | 1 + .../Model/UserLocationDetails.cs | 2 +- .../Location/Locations.API/Startup.cs | 75 ++++++-- .../Location/Locations.API/appsettings.json | 4 +- .../MarketingDetailsHttpTrigger/function.json | 24 +++ .../MarketingDetailsHttpTrigger/run.csx | 87 +++++++++ .../Infrastructure/AzureFunctions/host.json | 1 + .../AzureFunctions/local.settings.json | 16 ++ .../AzureFunctions/marketing-functions.csproj | 27 +++ .../Controllers/CampaignsController.cs | 9 +- .../Marketing.API/Dto/CampaignDTO.cs | 1 + .../Infrastructure/MarketingContextSeed.cs | 2 + ...9102516_added-campaign-details.Designer.cs | 121 +++++++++++++ .../20170629102516_added-campaign-details.cs | 33 ++++ .../MarketingContextModelSnapshot.cs | 4 + .../UserLocationUpdatedIntegrationEvent.cs | 10 +- ...rLocationUpdatedIntegrationEventHandler.cs | 13 +- .../Marketing.API/Marketing.API.csproj | 3 +- .../Marketing.API/MarketingSettings.cs | 1 + .../Marketing/Marketing.API/Model/Campaign.cs | 4 + .../Model/UserLocationDetails.cs | 2 +- .../Marketing/Marketing.API/Program.cs | 2 +- .../Marketing/Marketing.API/Startup.cs | 100 +++++++---- .../Marketing/Marketing.API/appsettings.json | 4 +- src/Services/Ordering/Ordering.API/Startup.cs | 3 - .../Ordering/Ordering.API/settings.json | 2 +- .../Payment/Payment.API/Payment.API.csproj | 1 + src/Services/Payment/Payment.API/Startup.cs | 58 ++++-- .../Payment/Payment.API/appsettings.json | 4 +- src/Web/WebMVC/AppSettings.cs | 1 + .../WebMVC/Controllers/CampaignsController.cs | 14 +- src/Web/WebMVC/ViewModels/CampaignItem.cs | 1 + src/Web/WebMVC/Views/Campaigns/Index.cshtml | 4 +- .../WebMVC/Views/Campaigns/_campaign.cshtml | 11 +- src/Web/WebMVC/appsettings.json | 1 + .../Services/Marketing/MarketingScenarios.cs | 4 +- .../Services/Marketing/appsettings.json | 8 +- 55 files changed, 846 insertions(+), 416 deletions(-) delete mode 100644 docker-compose.vs.debug.yml delete mode 100644 docker-compose.vs.release.yml create mode 100644 src/Services/Location/Locations.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs create mode 100644 src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/function.json create mode 100644 src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/run.csx create mode 100644 src/Services/Marketing/Infrastructure/AzureFunctions/host.json create mode 100644 src/Services/Marketing/Infrastructure/AzureFunctions/local.settings.json create mode 100644 src/Services/Marketing/Infrastructure/AzureFunctions/marketing-functions.csproj create mode 100644 src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.Designer.cs create mode 100644 src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.cs diff --git a/.env b/.env index d9482516b..20bcc943f 100644 --- a/.env +++ b/.env @@ -5,4 +5,14 @@ # The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices ESHOP_EXTERNAL_DNS_NAME_OR_IP=localhost -ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.92 \ No newline at end of file +ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP=10.121.122.92 + +ESHOP_AZURE_REDIS_BASKET_DB=eshopredisez55a72p6wm62.redis.cache.windows.net:6379,password=RnA4HSjsZY24c+BOVLopWI5lQI5njw1WliiRz3U5Om0=,ssl=False,abortConnect=False +ESHOP_AZURE_STORAGE_ACCOUNT=https://catalogez55a72p6wm62.blob.core.windows.net/pics/ +ESHOP_AZURE_SERVICE_BUS=Endpoint=sb://eshopsbez55a72p6wm62.servicebus.windows.net/;SharedAccessKeyName=Root;SharedAccessKey=Jd6iRe3rzTWgMXDDdyUz/AcL6YxLzb8X9afSNPnq50k=;EntityPath=eshop_event_bus +ESHOP_AZURE_COSMOSDB=mongodb://eshop-nosqlez55a72p6wm62:l8jMNoLHFXOijAvaVMjHeCwHK8gAR9SovuK86uCOvwfnMhuhwytPKByOPqrQrlsDz9RPFet2J6SzEbBQXLZokA==@eshop-nosqlez55a72p6wm62.documents.azure.com:10255/?ssl=true&replicaSet=globaldb +ESHOP_AZURE_CATALOG_DB=Server=tcp:eshopsql-ez55a72p6wm62.database.windows.net,1433;Initial Catalog=catalogdb;Persist Security Info=False;User ID=eshop;Password=Pass@word!;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; +ESHOP_AZURE_IDENTITY_DB=Server=tcp:eshopsql-ez55a72p6wm62.database.windows.net,1433;Initial Catalog=identitydb;Persist Security Info=False;User ID=eshop;Password=Pass@word!;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; +ESHOP_AZURE_ORDERING_DB=Server=tcp:eshopsql-ez55a72p6wm62.database.windows.net,1433;Initial Catalog=orderingdb;Persist Security Info=False;User ID=eshop;Password=Pass@word!;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; +ESHOP_AZURE_MARKETING_DB=Server=tcp:eshopsql-ez55a72p6wm62.database.windows.net,1433;Initial Catalog=marketingdb;Persist Security Info=False;User ID=eshop;Password=Pass@word!;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30; +ESHOP_AZUREFUNC_CAMPAIGN_DETAILS_URI=https://marketing-functions.azurewebsites.net/api/MarketingDetailsHttpTrigger?code=LhqDGSKZIHM0vwBwjHHRv6umvcuRqEtB0m8laxPkb1g1IycLyWmW1A== \ No newline at end of file diff --git a/deploy/az/servicebus/sbusdeploy.json b/deploy/az/servicebus/sbusdeploy.json index 5f6ce3c90..50c1c6c54 100644 --- a/deploy/az/servicebus/sbusdeploy.json +++ b/deploy/az/servicebus/sbusdeploy.json @@ -14,6 +14,10 @@ "BasketSubscriptionName": "Basket", "CatalogSubscriptionName": "Catalog", "OrderingSubscriptionName": "Ordering", + "LocationsSubscriptionName": "Locations", + "MarketingSubscriptionName": "Marketing", + "GracePeriodSubscriptionName": "GracePeriod", + "PaymentSubscriptionName": "Payment", "location": "[resourceGroup().location]", "sbVersion": "2015-08-01", "defaultSASKeyName": "Root", @@ -130,6 +134,86 @@ "autoDeleteOnIdle": "10675199.02:48:05.4775807", "entityAvailabilityStatus": "Available" } + }, + { + "apiVersion": "[variables('sbVersion')]", + "name": "[variables('LocationsSubscriptionName')]", + "type": "Subscriptions", + "dependsOn": [ + "[variables('serviceBusTopicName')]" + ], + "properties": { + "lockDuration": "00:00:30", + "requiresSession": false, + "defaultMessageTimeToLive": "14.00:00:00", + "deadLetteringOnMessageExpiration": true, + "deadLetteringOnFilterEvaluationExceptions": true, + "maxDeliveryCount": 10, + "enableBatchedOperations": false, + "status": "Active", + "autoDeleteOnIdle": "10675199.02:48:05.4775807", + "entityAvailabilityStatus": "Available" + } + }, + { + "apiVersion": "[variables('sbVersion')]", + "name": "[variables('MarketingSubscriptionName')]", + "type": "Subscriptions", + "dependsOn": [ + "[variables('serviceBusTopicName')]" + ], + "properties": { + "lockDuration": "00:00:30", + "requiresSession": false, + "defaultMessageTimeToLive": "14.00:00:00", + "deadLetteringOnMessageExpiration": true, + "deadLetteringOnFilterEvaluationExceptions": true, + "maxDeliveryCount": 10, + "enableBatchedOperations": false, + "status": "Active", + "autoDeleteOnIdle": "10675199.02:48:05.4775807", + "entityAvailabilityStatus": "Available" + } + }, + { + "apiVersion": "[variables('sbVersion')]", + "name": "[variables('GracePeriodSubscriptionName')]", + "type": "Subscriptions", + "dependsOn": [ + "[variables('serviceBusTopicName')]" + ], + "properties": { + "lockDuration": "00:00:30", + "requiresSession": false, + "defaultMessageTimeToLive": "14.00:00:00", + "deadLetteringOnMessageExpiration": true, + "deadLetteringOnFilterEvaluationExceptions": true, + "maxDeliveryCount": 10, + "enableBatchedOperations": false, + "status": "Active", + "autoDeleteOnIdle": "10675199.02:48:05.4775807", + "entityAvailabilityStatus": "Available" + } + }, + { + "apiVersion": "[variables('sbVersion')]", + "name": "[variables('PaymentSubscriptionName')]", + "type": "Subscriptions", + "dependsOn": [ + "[variables('serviceBusTopicName')]" + ], + "properties": { + "lockDuration": "00:00:30", + "requiresSession": false, + "defaultMessageTimeToLive": "14.00:00:00", + "deadLetteringOnMessageExpiration": true, + "deadLetteringOnFilterEvaluationExceptions": true, + "maxDeliveryCount": 10, + "enableBatchedOperations": false, + "status": "Active", + "autoDeleteOnIdle": "10675199.02:48:05.4775807", + "entityAvailabilityStatus": "Available" + } } ] } diff --git a/docker-compose.dcproj b/docker-compose.dcproj index f7f04c5b4..23547b577 100644 --- a/docker-compose.dcproj +++ b/docker-compose.dcproj @@ -5,6 +5,8 @@ True http://localhost:5100 webmvc + Linux + 2.0 @@ -14,12 +16,6 @@ docker-compose.yml - - docker-compose.yml - - - docker-compose.yml - \ No newline at end of file diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 3d7b3a134..c60645b2e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -9,14 +9,14 @@ version: '3' services: graceperiodmanager: environment: - - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - - EventBusConnection=rabbitmq + - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word} + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} basket.api: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - - ConnectionString=${ESHOP_AZURE_REDIS:-basket.data} + - ConnectionString=${ESHOP_AZURE_REDIS_BASKET_DB:-basket.data} - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} ports: @@ -26,7 +26,7 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word + - ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word} - PicBaseUrl=${ESHOP_AZURE_STORAGE_ACCOUNT:-http://localhost:5101/api/v1/pic/} #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} ports: @@ -38,7 +38,7 @@ services: - ASPNETCORE_URLS=http://0.0.0.0:80 - SpaClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5104 - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback #localhost do not work for UWP login, so we have to use "external" IP always - - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word + - ConnectionStrings__DefaultConnection=${ESHOP_AZURE_IDENTITY_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word} - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100 #Local: You need to open your local dev-machine firewall at range 5100-5105. ports: - "5105:80" @@ -47,7 +47,7 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word + - ConnectionString=${ESHOP_AZURE_ORDERING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word} - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} ports: @@ -57,15 +57,16 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word - - MongoConnectionString=mongodb://nosql.data + - ConnectionString=${ESHOP_AZURE_MARKETING_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word} + - MongoConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} - MongoDatabase=MarketingDb - - EventBusConnection=rabbitmq + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} - ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - CampaignDetailFunctionUri=${ESHOP_AZUREFUNC_CAMPAIGN_DETAILS_URI} ports: - "5110:80" - + webspa: environment: - ASPNETCORE_ENVIRONMENT=Development @@ -123,7 +124,7 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:5108 - - EventBusConnection=rabbitmq + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} ports: - "5108:80" @@ -131,9 +132,9 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - - ConnectionString=mongodb://nosql.data + - ConnectionString=${ESHOP_AZURE_COSMOSDB:-mongodb://nosql.data} - Database=LocationsDb - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - - EventBusConnection=rabbitmq + - EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq} ports: - "5109:80" diff --git a/docker-compose.vs.debug.yml b/docker-compose.vs.debug.yml deleted file mode 100644 index b0c792d32..000000000 --- a/docker-compose.vs.debug.yml +++ /dev/null @@ -1,166 +0,0 @@ -version: '3' - -services: - basket.api: - image: eshop/basket.api:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Services/Basket/Basket.API:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - catalog.api: - image: eshop/catalog.api:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Services/Catalog/Catalog.API:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - identity.api: - image: eshop/identity.api:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Services/Identity/Identity.API:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - ordering.api: - image: eshop/ordering.api:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Services/Ordering/Ordering.API:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - marketing.api: - image: eshop/marketing.api:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Services/Marketing/Marketing.API:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - - webspa: - image: eshop/webspa:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Web/WebSPA:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - webmvc: - image: eshop/webmvc:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Web/WebMVC:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - webstatus: - image: eshop/webstatus:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Web/WebStatus:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - payment.api: - image: eshop/payment.api:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Services/Payment/Payment.API:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - graceperiodmanager: - image: eshop/graceperiodmanager:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ./src/Services/GracePeriod/GracePeriodManager:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - locations.api: - image: eshop/locations.api:dev - build: - args: - source: ${DOCKER_BUILD_SOURCE} - environment: - - DOTNET_USE_POLLING_FILE_WATCHER=1 - volumes: - - ./src/Services/Location/Locations.API:/app - - ~/.nuget/packages:/root/.nuget/packages:ro - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" diff --git a/docker-compose.vs.release.yml b/docker-compose.vs.release.yml deleted file mode 100644 index 6aac3f799..000000000 --- a/docker-compose.vs.release.yml +++ /dev/null @@ -1,112 +0,0 @@ -version: '3' - -services: - basket.api: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - catalog.api: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - identity.api: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - ordering.api: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - marketing.api: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - webspa: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - webmvc: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - webstatus: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - payment.api: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - graceperiodmanager: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" - - locations.api: - build: - args: - source: ${DOCKER_BUILD_SOURCE} - volumes: - - ~/clrdbg:/clrdbg:ro - entrypoint: tail -f /dev/null - labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 19ecd4787..b5ccabdac 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.13 +VisualStudioVersion = 15.0.26608.5 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}" EndProject @@ -98,6 +98,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.API", "src\Servic EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBusServiceBus", "src\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj", "{69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{5B1011EC-CEE5-47AA-B336-99381D573679}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AzureFunctions", "AzureFunctions", "{106B787C-2CFF-4484-8C07-D14589859E94}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "marketing-functions", "src\Services\Marketing\Infrastructure\AzureFunctions\marketing-functions.csproj", "{E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1312,6 +1318,54 @@ Global {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x64.Build.0 = Release|Any CPU {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x86.ActiveCfg = Release|Any CPU {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8}.Release|x86.Build.0 = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|ARM.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|iPhone.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|x64.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|x64.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|x86.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.AppStore|x86.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|ARM.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|iPhone.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|x64.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|x64.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|x86.ActiveCfg = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Debug|x86.Build.0 = Debug|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|ARM.ActiveCfg = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|ARM.Build.0 = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|iPhone.ActiveCfg = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|iPhone.Build.0 = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|x64.ActiveCfg = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|x64.Build.0 = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|x86.ActiveCfg = Release|Any CPU + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1359,5 +1413,8 @@ Global {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} {DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} {69AF10D3-AA76-4FF7-B187-EC7E8CC5F5B8} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} + {5B1011EC-CEE5-47AA-B336-99381D573679} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} + {106B787C-2CFF-4484-8C07-D14589859E94} = {5B1011EC-CEE5-47AA-B336-99381D573679} + {E7F8F7C0-9FE5-4DAB-B510-1F05ADD11E1F} = {106B787C-2CFF-4484-8C07-D14589859E94} EndGlobalSection EndGlobal diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index 9ac2f9bd4..771ed212d 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -82,10 +82,10 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API { services.AddSingleton(sp => { - var settings = sp.GetRequiredService>().Value; var logger = sp.GetRequiredService>(); - var serviceBusConnection = new ServiceBusConnectionStringBuilder(settings.EventBusConnection); + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); }); @@ -94,17 +94,19 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API { services.AddSingleton(sp => { - var settings = sp.GetRequiredService>().Value; var logger = sp.GetRequiredService>(); + var factory = new ConnectionFactory() { - HostName = settings.EventBusConnection + HostName = Configuration["EventBusConnection"] }; return new DefaultRabbitMQPersistentConnection(factory, logger); }); } + RegisterEventBus(services); + services.AddSwaggerGen(options => { @@ -130,7 +132,6 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API services.AddTransient(); services.AddTransient(); - RegisterEventBus(services); services.AddOptions(); var container = new ContainerBuilder(); diff --git a/src/Services/Basket/Basket.API/appsettings.json b/src/Services/Basket/Basket.API/appsettings.json index f062f76d7..d36acfa8a 100644 --- a/src/Services/Basket/Basket.API/appsettings.json +++ b/src/Services/Basket/Basket.API/appsettings.json @@ -9,6 +9,6 @@ }, "IdentityUrl": "http://localhost:5105", "ConnectionString": "127.0.0.1", - "AzureServiceBusEnabled": false, + "AzureServiceBusEnabled": true, "SubscriptionClientName": "Basket" } \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/settings.json b/src/Services/Catalog/Catalog.API/settings.json index f21be6e5d..7f09b6436 100644 --- a/src/Services/Catalog/Catalog.API/settings.json +++ b/src/Services/Catalog/Catalog.API/settings.json @@ -9,7 +9,7 @@ "Microsoft": "Information" } }, - "AzureServiceBusEnabled": false, + "AzureServiceBusEnabled": true, "AzureStorageEnabled": true, "SubscriptionClientName": "Catalog" } diff --git a/src/Services/GracePeriod/GracePeriodManager/GracePeriodManager.csproj b/src/Services/GracePeriod/GracePeriodManager/GracePeriodManager.csproj index d057a6b54..0fa866642 100644 --- a/src/Services/GracePeriod/GracePeriodManager/GracePeriodManager.csproj +++ b/src/Services/GracePeriod/GracePeriodManager/GracePeriodManager.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Services/GracePeriod/GracePeriodManager/Program.cs b/src/Services/GracePeriod/GracePeriodManager/Program.cs index eecdd80ea..88fb1547e 100644 --- a/src/Services/GracePeriod/GracePeriodManager/Program.cs +++ b/src/Services/GracePeriod/GracePeriodManager/Program.cs @@ -14,6 +14,8 @@ using Microsoft.Extensions.Options; using RabbitMQ.Client; using Services; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; + using Microsoft.Azure.ServiceBus; public class Program { @@ -58,20 +60,36 @@ services.AddLogging() .AddOptions() .Configure(Configuration) - .AddSingleton() - .AddSingleton(sp => + .AddSingleton(); + + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); + + return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); + }); + } + else + { + services.AddSingleton(sp => { - var settings = sp.GetRequiredService>().Value; var logger = sp.GetRequiredService>(); + var factory = new ConnectionFactory() { - HostName = settings.EventBusConnection + HostName = Configuration["EventBusConnection"] }; return new DefaultRabbitMQPersistentConnection(factory, logger); }); + } - RegisterEventBus(services); + RegisterEventBus(services); var container = new ContainerBuilder(); container.Populate(services); @@ -87,7 +105,25 @@ private static void RegisterEventBus(IServiceCollection services) { - services.AddSingleton(); + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + var subscriptionClientName = Configuration["SubscriptionClientName"]; + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); + }); + } + else + { + services.AddSingleton(); + } + services.AddSingleton(); } } diff --git a/src/Services/GracePeriod/GracePeriodManager/appsettings.json b/src/Services/GracePeriod/GracePeriodManager/appsettings.json index 273437a8e..4e2ca9b72 100644 --- a/src/Services/GracePeriod/GracePeriodManager/appsettings.json +++ b/src/Services/GracePeriod/GracePeriodManager/appsettings.json @@ -9,5 +9,7 @@ }, "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", "GracePeriodTime": "1", - "CheckUpdateTime": "30000" + "CheckUpdateTime": "30000", + "AzureServiceBusEnabled": true, + "SubscriptionClientName": "GracePeriod" } diff --git a/src/Services/Identity/Identity.API/Configuration/Config.cs b/src/Services/Identity/Identity.API/Configuration/Config.cs index 4aa12d4ce..035ac5177 100644 --- a/src/Services/Identity/Identity.API/Configuration/Config.cs +++ b/src/Services/Identity/Identity.API/Configuration/Config.cs @@ -114,6 +114,21 @@ namespace Identity.API.Configuration "locations", "marketing" }, + }, + new Client + { + ClientId = "swaggerui", + ClientName = "Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { "http://localhost:5109/swagger/o2c.html" }, + PostLogoutRedirectUris = { "http://localhost:5109/swagger/" }, + + AllowedScopes = + { + "locations" + } } }; } diff --git a/src/Services/Location/Locations.API/Controllers/LocationsController.cs b/src/Services/Location/Locations.API/Controllers/LocationsController.cs index a6cb8bdad..a61c7f2d0 100644 --- a/src/Services/Location/Locations.API/Controllers/LocationsController.cs +++ b/src/Services/Location/Locations.API/Controllers/LocationsController.cs @@ -46,7 +46,7 @@ namespace Locations.API.Controllers var location = await _locationsService.GetLocation(locationId); return Ok(location); } - + //POST api/v1/[controller]/ [Route("")] [HttpPost] diff --git a/src/Services/Location/Locations.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs b/src/Services/Location/Locations.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs new file mode 100644 index 000000000..5a9d9e12a --- /dev/null +++ b/src/Services/Location/Locations.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs @@ -0,0 +1,31 @@ + +using Microsoft.AspNetCore.Authorization; +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Filters +{ + internal class AuthorizeCheckOperationFilter : IOperationFilter + { + public void Apply(Operation operation, OperationFilterContext context) + { + // Check for authorize attribute + var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType().Any() || + context.ApiDescription.ActionAttributes().OfType().Any(); + + if (hasAuthorize) + { + operation.Responses.Add("401", new Response { Description = "Unauthorized" }); + operation.Responses.Add("403", new Response { Description = "Forbidden" }); + + operation.Security = new List>>(); + operation.Security.Add(new Dictionary> + { + { "oauth2", new [] { "api1" } } + }); + } + } + } +} diff --git a/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs b/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs index 4b88e1927..399264428 100644 --- a/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs +++ b/src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs @@ -1,15 +1,15 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services { + using global::Locations.API.IntegrationEvents.Events; + using global::Locations.API.Model; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions; using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories; - using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel; using Microsoft.eShopOnContainers.Services.Locations.API.Model; + using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel; using System; - using System.Threading.Tasks; - using System.Linq; - using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions; using System.Collections.Generic; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.Services.Locations.API.IntegrationEvents.Events; + using System.Threading.Tasks; public class LocationsService : ILocationsService { diff --git a/src/Services/Location/Locations.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs b/src/Services/Location/Locations.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs index d4112a54d..4c8655246 100644 --- a/src/Services/Location/Locations.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs +++ b/src/Services/Location/Locations.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs @@ -1,13 +1,13 @@ -namespace Microsoft.eShopOnContainers.Services.Locations.API.IntegrationEvents.Events +namespace Locations.API.IntegrationEvents.Events { + using Locations.API.Model; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; - using Microsoft.eShopOnContainers.Services.Locations.API.Model; using System.Collections.Generic; public class UserLocationUpdatedIntegrationEvent : IntegrationEvent { - public string UserId { get; private set; } - public List LocationList { get; private set; } + public string UserId { get; set; } + public List LocationList { get; set; } public UserLocationUpdatedIntegrationEvent(string userId, List locationList) { diff --git a/src/Services/Location/Locations.API/Locations.API.csproj b/src/Services/Location/Locations.API/Locations.API.csproj index b9ef671dc..2e012bdcb 100644 --- a/src/Services/Location/Locations.API/Locations.API.csproj +++ b/src/Services/Location/Locations.API/Locations.API.csproj @@ -40,6 +40,7 @@ + diff --git a/src/Services/Location/Locations.API/Model/UserLocationDetails.cs b/src/Services/Location/Locations.API/Model/UserLocationDetails.cs index 6e152fe1b..a1b67e240 100644 --- a/src/Services/Location/Locations.API/Model/UserLocationDetails.cs +++ b/src/Services/Location/Locations.API/Model/UserLocationDetails.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Microsoft.eShopOnContainers.Services.Locations.API.Model +namespace Locations.API.Model { public class UserLocationDetails { diff --git a/src/Services/Location/Locations.API/Startup.cs b/src/Services/Location/Locations.API/Startup.cs index f767f227b..6a6f10935 100644 --- a/src/Services/Location/Locations.API/Startup.cs +++ b/src/Services/Location/Locations.API/Startup.cs @@ -16,6 +16,10 @@ using Microsoft.Extensions.Logging; using RabbitMQ.Client; using System.Reflection; using System; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +using Microsoft.Azure.ServiceBus; +using System.Collections.Generic; +using Swashbuckle.AspNetCore.Swagger; namespace Microsoft.eShopOnContainers.Services.Locations.API { @@ -51,19 +55,34 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API services.Configure(Configuration); - services.AddSingleton(sp => + if (Configuration.GetValue("AzureServiceBusEnabled")) { - var logger = sp.GetRequiredService>(); + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - var factory = new ConnectionFactory() + return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); + }); + } + else + { + services.AddSingleton(sp => { - HostName = Configuration["EventBusConnection"] - }; + var logger = sp.GetRequiredService>(); - return new DefaultRabbitMQPersistentConnection(factory, logger); - }); + var factory = new ConnectionFactory() + { + HostName = Configuration["EventBusConnection"] + }; - RegisterServiceBus(services); + return new DefaultRabbitMQPersistentConnection(factory, logger); + }); + } + + RegisterEventBus(services); // Add framework services. services.AddSwaggerGen(options => @@ -76,6 +95,21 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API Description = "The Location Microservice HTTP API. This is a Data-Driven/CRUD microservice sample", TermsOfService = "Terms Of Service" }); + + options.AddSecurityDefinition("oauth2", new OAuth2Scheme + { + Type = "oauth2", + Flow = "implicit", + AuthorizationUrl = "http://localhost:5105/connect/authorize", + TokenUrl = "http://localhost:5105/connect/token", + Scopes = new Dictionary() + { + { "locations", "Locations API" } + } + }); + + options.OperationFilter(); + }); services.AddCors(options => @@ -117,6 +151,7 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API .UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + c.ConfigureOAuth2("swaggerui", "", "", "Swagger UI"); }); LocationsContextSeed.SeedAsync(app, loggerFactory) @@ -134,10 +169,28 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API }); } - private void RegisterServiceBus(IServiceCollection services) + private void RegisterEventBus(IServiceCollection services) { - services.AddSingleton(); + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + var subscriptionClientName = Configuration["SubscriptionClientName"]; + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); + }); + } + else + { + services.AddSingleton(); + } + services.AddSingleton(); - } + } } } diff --git a/src/Services/Location/Locations.API/appsettings.json b/src/Services/Location/Locations.API/appsettings.json index a280150a3..58b84eada 100644 --- a/src/Services/Location/Locations.API/appsettings.json +++ b/src/Services/Location/Locations.API/appsettings.json @@ -9,5 +9,7 @@ "System": "Information", "Microsoft": "Information" } - } + }, + "AzureServiceBusEnabled": true, + "SubscriptionClientName": "Locations" } \ No newline at end of file diff --git a/src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/function.json b/src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/function.json new file mode 100644 index 000000000..1d82965f0 --- /dev/null +++ b/src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/function.json @@ -0,0 +1,24 @@ +{ + "bindings": [ + { + "authLevel": "function", + "name": "req", + "type": "httpTrigger", + "direction": "in" + }, + { + "name": "$return", + "type": "http", + "direction": "out" + }, + { + "type": "documentDB", + "name": "inputDocument", + "databaseName": "MarketingDb", + "collectionName": "MarketingReadDataModel", + "connection": "eshop-nosqlez55a72p6wm62_DOCUMENTDB", + "direction": "in" + } + ], + "disabled": false +} \ No newline at end of file diff --git a/src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/run.csx b/src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/run.csx new file mode 100644 index 000000000..b4aa70666 --- /dev/null +++ b/src/Services/Marketing/Infrastructure/AzureFunctions/MarketingDetailsHttpTrigger/run.csx @@ -0,0 +1,87 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Configuration; +using Dapper; +using System.Data.SqlClient; + +public static async Task Run(HttpRequestMessage req, dynamic inputDocument, TraceWriter log) +{ + log.Info($"Campaign HTTP trigger function processed a request. RequestUri={req.RequestUri}"); + + string htmlResponse = string.Empty; + + // parse query parameter + string campaignId = req.GetQueryNameValuePairs() + .FirstOrDefault(q => string.Compare(q.Key, "campaignId", true) == 0) + .Value; + + string userId = req.GetQueryNameValuePairs() + .FirstOrDefault(q => string.Compare(q.Key, "userId", true) == 0) + .Value; + + var cnnString = ConfigurationManager.ConnectionStrings["SqlConnection"].ConnectionString; + + using (var conn = new SqlConnection(cnnString)) + { + await conn.OpenAsync(); + var sql = "SELECT * FROM [dbo].[Campaign] WHERE Id = @CampaignId;"; + var campaign = (await conn.QueryAsync(sql, new { CampaignId = campaignId })).FirstOrDefault(); + htmlResponse = BuildHtmlResponse(campaign); + } + + var response = new HttpResponseMessage(HttpStatusCode.OK); + response.Content = new ByteArrayContent(System.Text.Encoding.UTF8.GetBytes(htmlResponse)); + response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + + return response; +} + +private static string BuildHtmlResponse(Campaign campaign) +{ + return string.Format(@" + + + + +
+ Campaign Details +
+ +
+
+
+
+ Card image cap +
+

{1}

+

{2}

+ +
+
+
+
+ + ", + campaign.PictureUri, + campaign.Name, + campaign.Description, + campaign.From.ToString("MMMM dd, yyyy"), + campaign.From.ToString("MMMM dd, yyyy")); +} + +public class Campaign +{ + public int Id { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public DateTime From { get; set; } + + public DateTime To { get; set; } + + public string PictureUri { get; set; } +} \ No newline at end of file diff --git a/src/Services/Marketing/Infrastructure/AzureFunctions/host.json b/src/Services/Marketing/Infrastructure/AzureFunctions/host.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/src/Services/Marketing/Infrastructure/AzureFunctions/host.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/Services/Marketing/Infrastructure/AzureFunctions/local.settings.json b/src/Services/Marketing/Infrastructure/AzureFunctions/local.settings.json new file mode 100644 index 000000000..fbc388b34 --- /dev/null +++ b/src/Services/Marketing/Infrastructure/AzureFunctions/local.settings.json @@ -0,0 +1,16 @@ +{ + "IsEncrypted": false, + "Value": { + "APPSETTING_eshop-nosqlez55a72p6wm62_DOCUMENTDB": "AccountEndpoint=https://eshop-nosqlez55a72p6wm62.documents.azure.com:443/;AccountKey=l8jMNoLHFXOijAvaVMjHeCwHK8gAR9SovuK86uCOvwfnMhuhwytPKByOPqrQrlsDz9RPFet2J6SzEbBQXLZokA==;", + "APPSETTING_FUNCTIONS_EXTENSION_VERSION": "~1", + "APPSETTING_ScmType": "None", + "APPSETTING_WEBSITE_AUTH_ENABLED": "False", + "APPSETTING_AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=marketingfuncti9f5e;AccountKey=BAxvLgEs0pEyVSUgsRV8okXZY6MM/0FSikYoXyk80zhLhzPe3gyHxf9QCKDZ1qa6LsgqYdF8yoSotlg3FoDvTw==", + "APPSETTING_WEBSITE_NODE_DEFAULT_VERSION": "6.5.0", + "APPSETTING_WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=marketingfuncti9f5e;AccountKey=BAxvLgEs0pEyVSUgsRV8okXZY6MM/0FSikYoXyk80zhLhzPe3gyHxf9QCKDZ1qa6LsgqYdF8yoSotlg3FoDvTw==", + "APPSETTING_WEBSITE_CONTENTSHARE": "marketing-functions9f5e", + "APPSETTING_WEBSITE_SLOT_NAME": "Production", + "APPSETTING_AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=marketingfuncti9f5e;AccountKey=BAxvLgEs0pEyVSUgsRV8okXZY6MM/0FSikYoXyk80zhLhzPe3gyHxf9QCKDZ1qa6LsgqYdF8yoSotlg3FoDvTw==", + "APPSETTING_WEBSITE_SITE_NAME": "marketing-functions" + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Infrastructure/AzureFunctions/marketing-functions.csproj b/src/Services/Marketing/Infrastructure/AzureFunctions/marketing-functions.csproj new file mode 100644 index 000000000..13019514c --- /dev/null +++ b/src/Services/Marketing/Infrastructure/AzureFunctions/marketing-functions.csproj @@ -0,0 +1,27 @@ + + + net461 + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index cbcbb382b..684c67edb 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -13,6 +13,7 @@ using AspNetCore.Authorization; using Extensions.Options; using Microsoft.eShopOnContainers.Services.Marketing.API.ViewModel; + using Microsoft.AspNetCore.Http; [Route("api/v1/[controller]")] [Authorize] @@ -21,14 +22,16 @@ private readonly MarketingContext _context; private readonly MarketingSettings _settings; private readonly IMarketingDataRepository _marketingDataRepository; + private readonly IHttpContextAccessor _httpContextAccessor; public CampaignsController(MarketingContext context, IMarketingDataRepository marketingDataRepository, - IOptionsSnapshot settings) + IOptionsSnapshot settings, IHttpContextAccessor httpContextAccessor) { _context = context; _marketingDataRepository = marketingDataRepository; _settings = settings.Value; + _httpContextAccessor = httpContextAccessor; } [HttpGet] @@ -175,6 +178,7 @@ private CampaignDTO MapCampaignModelToDto(Campaign campaign) { + var userId = _httpContextAccessor.HttpContext.User.FindFirst("sub").Value; return new CampaignDTO { Id = campaign.Id, @@ -182,7 +186,8 @@ Description = campaign.Description, From = campaign.From, To = campaign.To, - PictureUri = GetUriPlaceholder(campaign.PictureUri) + PictureUri = GetUriPlaceholder(campaign.PictureUri), + DetailsUri = $"{_settings.CampaignDetailFunctionUri}&campaignId={campaign.Id}$userId{userId}" }; } diff --git a/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs b/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs index 829011ce5..3c409c8a9 100644 --- a/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs +++ b/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs @@ -15,5 +15,6 @@ public DateTime To { get; set; } public string PictureUri { get; set; } + public string DetailsUri { get; set; } } } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs index fc8c02b4c..ecb3f3c80 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs @@ -38,6 +38,7 @@ From = DateTime.Now, To = DateTime.Now.AddDays(7), PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/campaigns/1/pic", + PictureName = "1.png", Rules = new List { new UserLocationRule @@ -54,6 +55,7 @@ From = DateTime.Now.AddDays(-7), To = DateTime.Now.AddDays(14), PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/campaigns/2/pic", + PictureName = "2.png", Rules = new List { new UserLocationRule diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.Designer.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.Designer.cs new file mode 100644 index 000000000..342d1d02f --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.Designer.cs @@ -0,0 +1,121 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations +{ + [DbContext(typeof(MarketingContext))] + [Migration("20170629102516_added-campaign-details")] + partial class addedcampaigndetails + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.2") + .HasAnnotation("SqlServer:Sequence:.campaign_hilo", "'campaign_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:.rule_hilo", "'rule_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "campaign_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Description") + .IsRequired() + .HasColumnName("Description"); + + b.Property("DetailsUri"); + + b.Property("From") + .HasColumnName("From"); + + b.Property("Name") + .IsRequired() + .HasColumnName("Name"); + + b.Property("PictureName"); + + b.Property("PictureUri") + .IsRequired() + .HasColumnName("PictureUri"); + + b.Property("To") + .HasColumnName("To"); + + b.HasKey("Id"); + + b.ToTable("Campaign"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "rule_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("CampaignId"); + + b.Property("Description") + .IsRequired() + .HasColumnName("Description"); + + b.Property("RuleTypeId"); + + b.HasKey("Id"); + + b.HasIndex("CampaignId"); + + b.ToTable("Rule"); + + b.HasDiscriminator("RuleTypeId"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.PurchaseHistoryRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + + b.ToTable("PurchaseHistoryRule"); + + b.HasDiscriminator().HasValue(2); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserLocationRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + b.Property("LocationId") + .HasColumnName("LocationId"); + + b.ToTable("UserLocationRule"); + + b.HasDiscriminator().HasValue(3); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserProfileRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + + b.ToTable("UserProfileRule"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", "Campaign") + .WithMany("Rules") + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.cs new file mode 100644 index 000000000..9d6ddf399 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170629102516_added-campaign-details.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations +{ + public partial class addedcampaigndetails : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DetailsUri", + table: "Campaign", + nullable: true); + + migrationBuilder.AddColumn( + name: "PictureName", + table: "Campaign", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DetailsUri", + table: "Campaign"); + + migrationBuilder.DropColumn( + name: "PictureName", + table: "Campaign"); + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/MarketingContextModelSnapshot.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/MarketingContextModelSnapshot.cs index bcac40659..3866a63ab 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/MarketingContextModelSnapshot.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/MarketingContextModelSnapshot.cs @@ -29,6 +29,8 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Mark .IsRequired() .HasColumnName("Description"); + b.Property("DetailsUri"); + b.Property("From") .HasColumnName("From"); @@ -36,6 +38,8 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Mark .IsRequired() .HasColumnName("Name"); + b.Property("PictureName"); + b.Property("PictureUri") .IsRequired() .HasColumnName("PictureUri"); diff --git a/src/Services/Marketing/Marketing.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs b/src/Services/Marketing/Marketing.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs index a7ab0cafd..8023031b1 100644 --- a/src/Services/Marketing/Marketing.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs +++ b/src/Services/Marketing/Marketing.API/IntegrationEvents/Events/UserLocationUpdatedIntegrationEvent.cs @@ -1,13 +1,13 @@ -namespace Microsoft.eShopOnContainers.Services.Marketing.API.IntegrationEvents.Events +namespace Marketing.API.IntegrationEvents.Events { - using Model; + using Marketing.API.Model; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using System.Collections.Generic; - using BuildingBlocks.EventBus.Events; public class UserLocationUpdatedIntegrationEvent : IntegrationEvent { - public string UserId { get; private set; } - public List LocationList { get; private set; } + public string UserId { get; set; } + public List LocationList { get; set; } public UserLocationUpdatedIntegrationEvent(string userId, List locationList) { diff --git a/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs b/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs index 7879c3d96..269018ab8 100644 --- a/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs +++ b/src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs @@ -1,12 +1,13 @@ -namespace Microsoft.eShopOnContainers.Services.Marketing.API.IntegrationEvents.Handlers +namespace Marketing.API.IntegrationEvents.Handlers { - using BuildingBlocks.EventBus.Abstractions; - using System.Threading.Tasks; - using Events; + using Marketing.API.IntegrationEvents.Events; + using Marketing.API.Model; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Repositories; + using Microsoft.eShopOnContainers.Services.Marketing.API.Model; using System; - using Infrastructure.Repositories; - using Model; using System.Collections.Generic; + using System.Threading.Tasks; public class UserLocationUpdatedIntegrationEventHandler : IIntegrationEventHandler diff --git a/src/Services/Marketing/Marketing.API/Marketing.API.csproj b/src/Services/Marketing/Marketing.API/Marketing.API.csproj index 4a6e2d4ea..067bb1ffc 100644 --- a/src/Services/Marketing/Marketing.API/Marketing.API.csproj +++ b/src/Services/Marketing/Marketing.API/Marketing.API.csproj @@ -5,7 +5,7 @@ 1.1.2 Exe ..\..\..\..\docker-compose.dcproj - Microsoft.eShopOnContainers.Services.Marketing.API + Marketing.API portable-net45+win8 aspnet-Marketing.API-20161122013619 @@ -56,6 +56,7 @@ + diff --git a/src/Services/Marketing/Marketing.API/MarketingSettings.cs b/src/Services/Marketing/Marketing.API/MarketingSettings.cs index f83200019..179d21ddf 100644 --- a/src/Services/Marketing/Marketing.API/MarketingSettings.cs +++ b/src/Services/Marketing/Marketing.API/MarketingSettings.cs @@ -6,5 +6,6 @@ public string MongoConnectionString { get; set; } public string MongoDatabase { get; set; } public string ExternalCatalogBaseUrl { get; set; } + public string CampaignDetailFunctionUri { get; set; } } } diff --git a/src/Services/Marketing/Marketing.API/Model/Campaign.cs b/src/Services/Marketing/Marketing.API/Model/Campaign.cs index 51a4c017c..841580427 100644 --- a/src/Services/Marketing/Marketing.API/Model/Campaign.cs +++ b/src/Services/Marketing/Marketing.API/Model/Campaign.cs @@ -15,8 +15,12 @@ public DateTime To { get; set; } + public string PictureName { get; set; } + public string PictureUri { get; set; } + public string DetailsUri { get; set; } + public List Rules { get; set; } diff --git a/src/Services/Marketing/Marketing.API/Model/UserLocationDetails.cs b/src/Services/Marketing/Marketing.API/Model/UserLocationDetails.cs index e6d7fe305..4de864681 100644 --- a/src/Services/Marketing/Marketing.API/Model/UserLocationDetails.cs +++ b/src/Services/Marketing/Marketing.API/Model/UserLocationDetails.cs @@ -1,4 +1,4 @@ -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model +namespace Marketing.API.Model { public class UserLocationDetails { diff --git a/src/Services/Marketing/Marketing.API/Program.cs b/src/Services/Marketing/Marketing.API/Program.cs index 2bf3b3d9c..31aefeeac 100644 --- a/src/Services/Marketing/Marketing.API/Program.cs +++ b/src/Services/Marketing/Marketing.API/Program.cs @@ -1,4 +1,4 @@ -namespace Microsoft.eShopOnContainers.Services.Marketing.API +namespace Marketing.API { using System.IO; using Microsoft.AspNetCore.Builder; diff --git a/src/Services/Marketing/Marketing.API/Startup.cs b/src/Services/Marketing/Marketing.API/Startup.cs index a31055d39..4c38e9cd1 100644 --- a/src/Services/Marketing/Marketing.API/Startup.cs +++ b/src/Services/Marketing/Marketing.API/Startup.cs @@ -1,28 +1,32 @@ -namespace Microsoft.eShopOnContainers.Services.Marketing.API +namespace Marketing.API { + using Autofac; + using Autofac.Extensions.DependencyInjection; + using global::Marketing.API.IntegrationEvents.Events; + using Marketing.API.IntegrationEvents.Handlers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; - using Microsoft.EntityFrameworkCore.Infrastructure; + using Microsoft.AspNetCore.Http; + using Microsoft.Azure.ServiceBus; using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Infrastructure; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; + using Microsoft.eShopOnContainers.Services.Marketing.API; using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Filters; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Repositories; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; - using System.Reflection; - using System; - using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Filters; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; - using RabbitMQ.Client; - using BuildingBlocks.EventBus.Abstractions; - using BuildingBlocks.EventBus; - using IntegrationEvents.Events; - using IntegrationEvents.Handlers; - using Infrastructure.Repositories; - using Autofac; - using Autofac.Extensions.DependencyInjection; using Polly; - using System.Threading.Tasks; + using RabbitMQ.Client; + using System; using System.Data.SqlClient; + using System.Reflection; + using System.Threading.Tasks; public class Startup { @@ -73,19 +77,32 @@ //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval }); - services.AddSingleton(sp => + if (Configuration.GetValue("AzureServiceBusEnabled")) { - var logger = sp.GetRequiredService>(); + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - var factory = new ConnectionFactory() + return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); + }); + } + else + { + services.AddSingleton(sp => { - HostName = Configuration["EventBusConnection"] - }; + var logger = sp.GetRequiredService>(); - return new DefaultRabbitMQPersistentConnection(factory, logger); - }); + var factory = new ConnectionFactory() + { + HostName = Configuration["EventBusConnection"] + }; - RegisterServiceBus(services); + return new DefaultRabbitMQPersistentConnection(factory, logger); + }); + } // Add framework services. services.AddSwaggerGen(options => @@ -109,7 +126,12 @@ .AllowCredentials()); }); + RegisterEventBus(services); + services.AddTransient(); + services.AddSingleton(); + + services.AddOptions(); //configure autofac var container = new ContainerBuilder(); @@ -134,7 +156,7 @@ .UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - }); + }); var context = (MarketingContext)app .ApplicationServices.GetService(typeof(MarketingContext)); @@ -155,21 +177,35 @@ }); } - private void RegisterServiceBus(IServiceCollection services) + private void RegisterEventBus(IServiceCollection services) { - services.AddSingleton(); - services.AddSingleton(); + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + var subscriptionClientName = Configuration["SubscriptionClientName"]; + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); + }); + } + else + { + services.AddSingleton(); + } - services.AddTransient, - UserLocationUpdatedIntegrationEventHandler>(); + services.AddSingleton(); + services.AddTransient(); } private void ConfigureEventBus(IApplicationBuilder app) { var eventBus = app.ApplicationServices.GetRequiredService(); - eventBus.Subscribe>(); + eventBus.Subscribe(); } private async Task WaitForSqlAvailabilityAsync(MarketingContext ctx, ILoggerFactory loggerFactory, IApplicationBuilder app, int retries = 0) diff --git a/src/Services/Marketing/Marketing.API/appsettings.json b/src/Services/Marketing/Marketing.API/appsettings.json index a05a01836..f630b0688 100644 --- a/src/Services/Marketing/Marketing.API/appsettings.json +++ b/src/Services/Marketing/Marketing.API/appsettings.json @@ -9,5 +9,7 @@ "MongoConnectionString": "mongodb://nosql.data", "MongoDatabase": "MarketingDb", "IdentityUrl": "http://localhost:5105", - "ExternalCatalogBaseUrl": "http://localhost:5110" + "ExternalCatalogBaseUrl": "http://localhost:5110", + "AzureServiceBusEnabled": true, + "SubscriptionClientName": "Marketing" } diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 42b479685..4c03355e4 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -4,11 +4,8 @@ using Autofac; using Autofac.Extensions.DependencyInjection; using global::Ordering.API.Application.IntegrationEvents; - using global::Ordering.API.Application.IntegrationEvents.EventHandling; using global::Ordering.API.Application.IntegrationEvents.Events; - using global::Ordering.API.Infrastructure.Middlewares; using Infrastructure; - using Infrastructure.Auth; using Infrastructure.AutofacModules; using Infrastructure.Filters; using Infrastructure.Services; diff --git a/src/Services/Ordering/Ordering.API/settings.json b/src/Services/Ordering/Ordering.API/settings.json index 9f1c58322..4c285e913 100644 --- a/src/Services/Ordering/Ordering.API/settings.json +++ b/src/Services/Ordering/Ordering.API/settings.json @@ -9,6 +9,6 @@ "Microsoft": "Information" } }, - "AzureServiceBusEnabled": false, + "AzureServiceBusEnabled": true, "SubscriptionClientName": "Ordering" } diff --git a/src/Services/Payment/Payment.API/Payment.API.csproj b/src/Services/Payment/Payment.API/Payment.API.csproj index dc8aad80d..3a1c4e248 100644 --- a/src/Services/Payment/Payment.API/Payment.API.csproj +++ b/src/Services/Payment/Payment.API/Payment.API.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Services/Payment/Payment.API/Startup.cs b/src/Services/Payment/Payment.API/Startup.cs index 32d0fc8c4..ddfa626d7 100644 --- a/src/Services/Payment/Payment.API/Startup.cs +++ b/src/Services/Payment/Payment.API/Startup.cs @@ -12,6 +12,8 @@ using RabbitMQ.Client; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Payment.API.IntegrationEvents.Events; using Payment.API.IntegrationEvents.EventHandling; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +using Microsoft.Azure.ServiceBus; namespace Payment.API { @@ -35,19 +37,34 @@ namespace Payment.API // Add framework services. services.AddMvc(); services.Configure(Configuration); - services.AddSingleton(sp => + if (Configuration.GetValue("AzureServiceBusEnabled")) { - var logger = sp.GetRequiredService>(); + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); + + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - var factory = new ConnectionFactory() + return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); + }); + } + else + { + services.AddSingleton(sp => { - HostName = Configuration["EventBusConnection"] - }; + var logger = sp.GetRequiredService>(); - return new DefaultRabbitMQPersistentConnection(factory, logger); - }); + var factory = new ConnectionFactory() + { + HostName = Configuration["EventBusConnection"] + }; + + return new DefaultRabbitMQPersistentConnection(factory, logger); + }); + } - RegisterServiceBus(services); + RegisterEventBus(services); services.AddSwaggerGen(options => { @@ -84,13 +101,28 @@ namespace Payment.API ConfigureEventBus(app); } - private void RegisterServiceBus(IServiceCollection services) + private void RegisterEventBus(IServiceCollection services) { - services.AddSingleton(); - services.AddSingleton(); + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + var subscriptionClientName = Configuration["SubscriptionClientName"]; + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); + }); + } + else + { + services.AddSingleton(); + } - services.AddTransient, - OrderStatusChangedToStockConfirmedIntegrationEventHandler>(); + services.AddSingleton(); } private void ConfigureEventBus(IApplicationBuilder app) diff --git a/src/Services/Payment/Payment.API/appsettings.json b/src/Services/Payment/Payment.API/appsettings.json index 601eaa246..e25d9df17 100644 --- a/src/Services/Payment/Payment.API/appsettings.json +++ b/src/Services/Payment/Payment.API/appsettings.json @@ -5,5 +5,7 @@ "Default": "Warning" } }, - "PaymentSucceded": "true" + "PaymentSucceded": "true", + "AzureServiceBusEnabled": true, + "SubscriptionClientName": "Payment" } diff --git a/src/Web/WebMVC/AppSettings.cs b/src/Web/WebMVC/AppSettings.cs index b15167647..58494cdd5 100644 --- a/src/Web/WebMVC/AppSettings.cs +++ b/src/Web/WebMVC/AppSettings.cs @@ -12,6 +12,7 @@ namespace Microsoft.eShopOnContainers.WebMVC public string OrderingUrl { get; set; } public string BasketUrl { get; set; } public string MarketingUrl { get; set; } + public bool ActivateCampaignDetailFunction { get; set; } public Logging Logging { get; set; } } diff --git a/src/Web/WebMVC/Controllers/CampaignsController.cs b/src/Web/WebMVC/Controllers/CampaignsController.cs index 8f291bb49..cbb08e051 100644 --- a/src/Web/WebMVC/Controllers/CampaignsController.cs +++ b/src/Web/WebMVC/Controllers/CampaignsController.cs @@ -11,19 +11,29 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers using System; using ViewModels.Pagination; using global::WebMVC.ViewModels; + using Microsoft.Extensions.Options; [Authorize] public class CampaignsController : Controller { private readonly ICampaignService _campaignService; + private readonly AppSettings _settings; - public CampaignsController(ICampaignService campaignService) => + public CampaignsController(ICampaignService campaignService, IOptionsSnapshot settings) + { _campaignService = campaignService; + _settings = settings.Value; + } public async Task Index(int page = 0, int pageSize = 10) { var campaignList = await _campaignService.GetCampaigns(pageSize, page); + if(campaignList is null) + { + return View(); + } + var totalPages = (int) Math.Ceiling((decimal) campaignList.Count / pageSize); var vm = new CampaignViewModel @@ -40,6 +50,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers } }; + ViewBag.IsCampaignDetailFunctionActive = _settings.ActivateCampaignDetailFunction; + return View(vm); } diff --git a/src/Web/WebMVC/ViewModels/CampaignItem.cs b/src/Web/WebMVC/ViewModels/CampaignItem.cs index a0bfbaf66..04e220229 100644 --- a/src/Web/WebMVC/ViewModels/CampaignItem.cs +++ b/src/Web/WebMVC/ViewModels/CampaignItem.cs @@ -15,5 +15,6 @@ public DateTime To { get; set; } public string PictureUri { get; set; } + public string DetailsUri { get; set; } } } \ No newline at end of file diff --git a/src/Web/WebMVC/Views/Campaigns/Index.cshtml b/src/Web/WebMVC/Views/Campaigns/Index.cshtml index d4c8abe48..bca288ed6 100644 --- a/src/Web/WebMVC/Views/Campaigns/Index.cshtml +++ b/src/Web/WebMVC/Views/Campaigns/Index.cshtml @@ -13,10 +13,8 @@ new Header() { Controller = "Catalog", Text = "Back to catalog" } })
- @if (Model.CampaignItems != null && Model.CampaignItems.Any()) + @if(Model != null && Model.CampaignItems.Any()) { - @Html.Partial("_pagination", Model.PaginationInfo) -
@foreach (var catalogItem in Model.CampaignItems) { diff --git a/src/Web/WebMVC/Views/Campaigns/_campaign.cshtml b/src/Web/WebMVC/Views/Campaigns/_campaign.cshtml index de3b52657..fd514d7c9 100644 --- a/src/Web/WebMVC/Views/Campaigns/_campaign.cshtml +++ b/src/Web/WebMVC/Views/Campaigns/_campaign.cshtml @@ -1,12 +1,17 @@ @model CampaignItem - -

@Model.Name

@Model.Name - + @if (ViewBag.IsCampaignDetailFunctionActive == true) + { + + } + else + { + + }