Browse Source

Merge pull request #23 from dotnet-architecture/dev

upd fork
pull/1934/head
Taras Kovalenko 6 years ago
committed by GitHub
parent
commit
7fe4a9c490
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
169 changed files with 5946 additions and 2506 deletions
  1. +2
    -0
      .dockerignore
  2. +1
    -0
      .gitignore
  3. +25
    -15
      README.md
  4. +1
    -1
      docker-compose-external.override.yml
  5. +1
    -1
      docker-compose-external.yml
  6. +1
    -1
      docker-compose-windows.prod.yml
  7. +1
    -1
      docker-compose-windows.yml
  8. +1
    -1
      docker-compose.ci.build.yml
  9. +1
    -0
      docker-compose.dcproj
  10. +1
    -1
      docker-compose.nobuild.yml
  11. +1
    -1
      docker-compose.override.windows.yml
  12. +84
    -43
      docker-compose.override.yml
  13. +88
    -48
      docker-compose.prod.yml
  14. +39
    -11
      docker-compose.yml
  15. BIN
      docs/Azure Dev eBook_Booklet.pdf
  16. BIN
      docs/MicrosoftAzure_StartGuide_Developers.pdf
  17. +184
    -51
      eShopOnContainers-ServicesAndWebApps.sln
  18. +7
    -7
      k8s/conf_cloud.yml
  19. +16
    -21
      k8s/conf_local.yml
  20. +37
    -37
      k8s/deploy.ps1
  21. +228
    -282
      k8s/deployments.yaml
  22. +0
    -47
      k8s/frontend.yaml
  23. +5
    -7
      k8s/gen-k8s-env-aks.ps1
  24. +23
    -19
      k8s/ingress.yaml
  25. +31
    -0
      k8s/internalurls.yaml
  26. +34
    -0
      k8s/ocelot/configuration-mobile-marketing.json
  27. +142
    -0
      k8s/ocelot/configuration-mobile-shopping.json
  28. +34
    -0
      k8s/ocelot/configuration-web-marketing.json
  29. +142
    -0
      k8s/ocelot/configuration-web-shopping.json
  30. +155
    -0
      k8s/ocelot/deployment.yaml
  31. +55
    -0
      k8s/ocelot/service.yaml
  32. +28
    -0
      k8s/services.yaml
  33. +19
    -0
      src/ApiGateways/ApiGw-Base/Dockerfile
  34. +16
    -0
      src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj
  35. +31
    -0
      src/ApiGateways/ApiGw-Base/Program.cs
  36. +27
    -0
      src/ApiGateways/ApiGw-Base/Properties/launchSettings.json
  37. +91
    -0
      src/ApiGateways/ApiGw-Base/Startup.cs
  38. +10
    -0
      src/ApiGateways/ApiGw-Base/appsettings.json
  39. +34
    -0
      src/ApiGateways/Mobile.Bff.Marketing/apigw/configuration.json
  40. +31
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs
  41. +133
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs
  42. +18
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs
  43. +42
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs
  44. +19
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
  45. +33
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs
  46. +27
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj
  47. +20
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs
  48. +31
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs
  49. +20
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs
  50. +33
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs
  51. +17
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs
  52. +31
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs
  53. +21
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs
  54. +36
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs
  55. +29
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Properties/launchSettings.json
  56. +53
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs
  57. +43
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs
  58. +15
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs
  59. +14
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs
  60. +13
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
  61. +37
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs
  62. +138
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs
  63. +15
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.json
  64. +8
    -0
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.localhost.json
  65. +142
    -0
      src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json
  66. +34
    -0
      src/ApiGateways/Web.Bff.Marketing/apigw/configuration.json
  67. +31
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs
  68. +133
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs
  69. +18
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs
  70. +42
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs
  71. +18
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile
  72. +33
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs
  73. +20
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs
  74. +31
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs
  75. +20
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs
  76. +33
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs
  77. +17
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs
  78. +31
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs
  79. +21
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs
  80. +36
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs
  81. +29
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Properties/launchSettings.json
  82. +53
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs
  83. +43
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs
  84. +15
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs
  85. +14
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs
  86. +13
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
  87. +37
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs
  88. +138
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs
  89. +27
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj
  90. +15
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.json
  91. +8
    -0
      src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.localhost.json
  92. +142
    -0
      src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json
  93. +5
    -0
      src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs
  94. +5
    -0
      src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs
  95. +0
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/ObservableExtension.cs
  96. +12
    -23
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs
  97. +15
    -7
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs
  98. +10
    -8
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogService.cs
  99. +2
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs
  100. +6
    -4
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs

+ 2
- 0
.dockerignore View File

@ -13,6 +13,7 @@ hosts
LICENSE
*.testsettings
vsts-docs
test
ServiceFabric
readme
k8s
@ -29,4 +30,5 @@ cli-linux
**/bower_components/
**/wwwroot/lib/
global.json
**/appsettings.localhost.json
src/Web/WebSPA/wwwroot/

+ 1
- 0
.gitignore View File

@ -260,3 +260,4 @@ pub/
/src/Web/WebMVC/wwwroot/lib
/src/Web/WebMVC/wwwroot/css/site.min.css
**/.kube/**
.mfractor

+ 25
- 15
README.md View File

@ -1,15 +1,19 @@
# eShopOnContainers - Microservices Architecture and Containers based Reference Application (**BETA state** - Visual Studio 2017 and CLI environments compatible)
Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers. <p>
Sample .NET Core reference application, powered by Microsoft, based on a simplified microservices architecture and Docker containers.
**NEWS / ANNOUNCEMENTS**
Do you want to be up-to-date on .NET Architecture guidance and reference apps like eShopOnContainers? --> Subscribe by "WATCHING" this new GitHub repo: https://github.com/dotnet-architecture/News
## IMPORTANT NOTES!
**Since April 5 2018, Visual Studio 2017 15.7 Preview 2.0 or later is needed to run the solution from the *[DEV branch](https://github.com/dotnet-architecture/eShopOnContainers/tree/dev)* (current evolving code) which now includes API Gateways features**. Due to the configuration used in `docker-compose.yml` file, VS 15.7 (currently in Preview 2) is needed to run the solution at [DEV branch](https://github.com/dotnet-architecture/eShopOnContainers/tree/dev) or you can also run eShopOnContainers with Docker CLI with **"docker-compose up"** or deploying to **Kubernetes/AKS**.
Trying to run the solution from [DEV branch](https://github.com/dotnet-architecture/eShopOnContainers/tree/dev) in VS 15.6 (current RTM of Visual Studio 2017) will generate errors (complaining about invalid values in docker-compose file).
**Note Visual Studio 2017 version required**: Please, use VS 2017 15.5 or later.
If you want/need to run eShopOnContainers in **Visual Studio 2017 15.6 RTM** or previous, you'll need to use the code at the **[MASTER branch](https://github.com/dotnet-architecture/eShopOnContainers/tree/master)** which is the previous stable version of eShopOnContainers.
**Note for Pull Requests**: We accept pull request from the community. When doing it, please do it onto the DEV branch which is the consolidated work-in-progress branch. Do not request it onto Master, if possible.
**Note for Pull Requests (PRs)**: We accept pull request from the community. When doing it, please do it onto the **DEV branch** which is the consolidated work-in-progress branch. Do not request it onto Master branch, if possible.
**NEWS / ANNOUNCEMENTS**
Do you want to be up-to-date on .NET Architecture guidance and reference apps like eShopOnContainers? --> Subscribe by "WATCHING" this new GitHub repo: https://github.com/dotnet-architecture/News
## Updated for .NET Core 2.0 "wave" of technologies
NOTE: We have migrated the whole server-side solution to .NET Core 2.0 "wave". Not just compilation but also new recommended code in EF Core 2.0, ASP.NET Core 2.0, and other new related versions.
eShopOnContainers is updated to .NET Core 2.0 "wave". Not just compilation but also new recommended code in EF Core 2.0, ASP.NET Core 2.0, and other new related versions.
The **dockerfiles** in the solution have also been updated and now support [**Docker Multi-Stage**](https://blogs.msdn.microsoft.com/stevelasker/2017/09/11/net-and-multistage-dockerfiles/) since mid-December 2017.
@ -22,35 +26,41 @@ For a list on the new .NET Core 2.0 related implemented features, see this [blog
>
> This reference application proposes a simplified microservice oriented architecture implementation to introduce technologies like .NET Core with Docker containers through a comprehensive application. The chosen domain is an eShop/eCommerce but simply because it is a well-know domain by most people/developers.
However, this sample application should not be considered as an "eCommerce reference model", at all. The implemented business domain might not be ideal from an eCommerce business point of view. It is neither trying to solve all the problems in a large, scalable and mission-critical distributed system. It is just a bootstrap for developers to easily get started in the world of Docker containers and microservices with .NET Core.
> <p>For example, the next step after running the solution in the local dev PC and understanding Docker containers and microservices development with .NET Core, is to select a microservice cluster/orchestrator like Kubernetes in Azure or Azure Service Fabric, both environments tested and supported by this solution.
> Additional steps would be to move your databases to HA cloud services (like Azure SQL Database), or to implement your EventBus with Azure Service Bus or any other production ready Service Bus in the market.
> <p>For example, the next step after running the solution in the local dev PC and understanding Docker containers and microservices development with .NET Core, is to select a microservice cluster/orchestrator like Kubernetes in Azure (AKS) or Azure Service Fabric, both environments tested and supported by this solution.
> Additional steps would be to move your databases to HA cloud services (like Azure SQL Database), or switch your EventBus to use Azure Service Bus (instead of bare-bone RabbitMQ) or any other production ready Service Bus in the market.
> <p>
> <img src="img/exploring-to-production-ready.png">
> Read the planned <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>Roadmap and Milestones for future releases of eShopOnContainers</a> within the Wiki for further info about possible new implementations and provide feedback at the <a href='https://github.com/dotnet/eShopOnContainers/issues'>ISSUES section</a> if you'd like to see any specific scenario implemented or improved. Also, feel free to discuss on any current issue.
**Architecture overview**: This reference application is cross-platform at the server and client side, thanks to .NET Core services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS or Windows/UWP plus any browser for the client web apps.
The architecture proposes a simplified microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the communication protocol between the client apps and the microservices and supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus (a light message broker, to choose between RabbitMQ or Azure Service Bus, underneath) plus other features defined at the <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>roadmap</a>.
### Architecture overview
This reference application is cross-platform at the server and client side, thanks to .NET Core services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS or Windows/UWP plus any browser for the client web apps.
The architecture proposes a microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the communication protocol between the client apps and the microservices and supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus (a light message broker, to choose between RabbitMQ or Azure Service Bus, underneath) plus other features defined at the <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>roadmap</a>.
<p>
<img src="img/eshop_logo.png">
<img src="img/eShopOnContainers_Architecture_Diagram.png">
<img src="https://user-images.githubusercontent.com/1712635/38758862-d4b42498-3f27-11e8-8dad-db60b0fa05d3.png">
<p>
> ### Important Note on API Gateways and published APIs
> Note that the previous architecture diagram shows how you deploy eShopOnContainers in a local Docker development machine. For a production-ready architecture we recommend to keep evolving your architecture with additional features like API Gateways based on AzureAPI Management or any other approach for API Gateways explained in the related documentation/eBook, so you can filter APIs and apply security in a single tier while hiding/securing the internal microservices to the client apps or outside consumers.
> Since April 2018, we have introduced the implementation of the [API Gateway pattern](http://microservices.io/patterns/apigateway.html) and [Backend-For-Front-End (BFF) pattern](https://samnewman.io/patterns/architectural/bff/) in eShopOnContainers architecture, so you can filter and publish simplified APIs and URIs and apply additional security in that tier while hiding/securing the internal microservices to the client apps or outside consumers. These sample API Gateways in eShopOnContainers are based on [Ocelot](https://github.com/ThreeMammals/Ocelot), an OSS lightweight API Gateway solution explained [here](http://threemammals.com/ocelot). The deployed API Gateways are autonomous and can be deployed as your own custom microservices/containers, as it is currently done in eShopOnContainers, so you can test it even in a simple development environment with just Docker engine or deploy it into orchestrators like Kubernetes in AKS or Service Fabric.
> For your production-ready architecture you can either keep using [Ocelot](https://github.com/ThreeMammals/Ocelot) which is simple and easy to use and used in production by significant companies or if you need further functionality and a much richer set of features suittable for commercial APIs, you can also substitute those API Gateways and use [Azure API Management](https://azure.microsoft.com/en-us/services/api-management/) or any other commercial API Gateway, as shown in the following image.
<p>
<img src="img/eShopOnContainers-Architecture-With-Azure-API-Management.png">
<p>
> The sample code in this repo is NOT making use of Azure API Management in order to be able to provide an "F5 experience" in Visual Studio (or CLI) of the sample with no up-front dependencies in Azure. But you should evaluate API Gateways alternatives when building for production.
> The sample code in this repo is NOT making use of Azure API Management in order to be able to provide an "F5 experience" in Visual Studio (or CLI) of the sample with no up-front dependencies in Azure. But you could evaluate API Gateways alternatives when building for production.
> ### Internal architecture and design of the microservices
The microservices are different in type, meaning different internal architecture pattern approaches depending on its purpose, as shown in the image below.
> The microservices are different in type, meaning different internal architecture pattern approaches depending on its purpose, as shown in the image below.
<p>
<img src="img/eShopOnContainers_Types_Of_Microservices.png">
<p>
<p>
> ### Important Note on Database Servers/Containers
> In this solution's current configuration for a development environment, the SQL databases are automatically deployed with sample data into a single SQL Server for Linux container (a single shared Docker container for SQL databases) so the whole solution can be up and running without any dependency to any cloud or specific server. Each database could also be deployed as a single Docker container, but then you'd need more than 8GB of RAM assigned to Docker in your development machine in order to be able to run 3 SQL Server Docker containers in your Docker Linux host in "Docker for Windows" or "Docker for Mac" development environments.
> In this solution's current configuration for a development environment, the SQL databases are automatically deployed with sample data into a single SQL Server container (a single shared Docker container for SQL databases) so the whole solution can be up and running without any dependency to any cloud or specific server. Each database could also be deployed as a single Docker container, but then you'd need more than 8GB of RAM assigned to Docker in your development machine in order to be able to run 3 SQL Server Docker containers in your Docker Linux host in "Docker for Windows" or "Docker for Mac" development environments.
> <p> A similar case is defined in regard to Redis cache running as a container for the development environment. Or a No-SQL database (MongoDB) running as a container.
> <p> However, in a real production environment it is recommended to have your databases (SQL Server, Redis, and the NO-SQL database, in this case) in HA (High Available) services like Azure SQL Database, Redis as a service and Azure CosmosDB instead the MongoDB container (as both systems share the same access protocol). If you want to change to a production configuration, you'll just need to change the connection strings once you have set up the servers in a HA cloud or on-premises.


+ 1
- 1
docker-compose-external.override.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
sql.data:


+ 1
- 1
docker-compose-external.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
sql.data:


+ 1
- 1
docker-compose-windows.prod.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
# The Production docker-compose file has to have the external/real IPs or DNS names for the services
# The ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like:


+ 1
- 1
docker-compose-windows.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
basket.api:


+ 1
- 1
docker-compose.ci.build.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
ci-build:


+ 1
- 0
docker-compose.dcproj View File

@ -7,6 +7,7 @@
<DockerServiceName>webmvc</DockerServiceName>
<DockerTargetOS>Linux</DockerTargetOS>
<ProjectVersion>2.1</ProjectVersion>
<DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
</PropertyGroup>
<ItemGroup>
<None Include=".dockerignore" />


+ 1
- 1
docker-compose.nobuild.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
basket.api:


+ 1
- 1
docker-compose.override.windows.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
# ONLY NEEDED WHEN RUNNING WINDOWS CONTAINERS
#


+ 84
- 43
docker-compose.override.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
# The default docker-compose.override file can use the "localhost" as the external name for testing web apps within the same dev machine.
# The ESHOP_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like:
@ -24,14 +24,15 @@ services:
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5103:80"
- "5103:80" # Important: In a production environment your should remove the external port (5103) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
catalog.api:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- ConnectionString=${ESHOP_AZURE_CATALOG_DB:-Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word}
- PicBaseUrl=${ESHOP_AZURE_STORAGE_CATALOG_URL:-http://localhost:5101/api/v1/catalog/items/[0]/pic/} #Local: You need to open your local dev-machine firewall at range 5100-5110.
- PicBaseUrl=${ESHOP_AZURE_STORAGE_CATALOG_URL:-http://localhost:5202/api/v1/c/catalog/items/[0]/pic/} #Local: You need to open your local dev-machine firewall at range 5100-5110.
- EventBusConnection=${ESHOP_AZURE_SERVICE_BUS:-rabbitmq}
- EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
- EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
@ -43,7 +44,8 @@ services:
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5101:80"
- "5101:80" # Important: In a production environment your should remove the external port (5101) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
identity.api:
environment:
@ -57,11 +59,13 @@ services:
- MarketingApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110
- BasketApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- 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
- UseCustomizationData=True
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5105:80"
- "5105:80"
ordering.api:
environment:
@ -81,25 +85,8 @@ services:
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5102:80"
ordering.backgroundtasks:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- 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}
- EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
- EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
- UseCustomizationData=True
- AzureServiceBusEnabled=False
- CheckUpdateTime=30000
- GracePeriodTime=1
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5111:80"
- "5102:80" # Important: In a production environment your should remove the external port (5102) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
marketing.api:
environment:
@ -123,18 +110,16 @@ services:
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5110:80"
- "5110:80" # Important: In a production environment your should remove the external port (5110) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
webspa:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110
- LocationsUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5109
- PurchaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5202
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5203
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
@ -145,18 +130,15 @@ services:
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5104:80"
- "5104:80"
webmvc:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api
- BasketUrl=http://basket.api
- LocationsUrl=http://locations.api
- PurchaseUrl=http://webshoppingapigw
- IdentityUrl=http://10.0.75.1:5105 # Local Mac: Use http://docker.for.mac.localhost:5105 || Local Windows: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. || #Remote access: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
- MarketingUrl=http://marketing.api
- MarketingUrl=http://webmarketingapigw
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
@ -199,7 +181,8 @@ services:
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5108:80"
- "5108:80" # Important: In a production environment your should remove the external port (5108) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
locations.api:
environment:
@ -217,25 +200,83 @@ services:
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5109:80"
- "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).
sql.data:
environment:
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
ports:
- "5433:1433"
- "5433:1433" # Important: In a production environment your should remove the external port
nosql.data:
ports:
- "27017:27017"
- "27017:27017" # Important: In a production environment your should remove the external port
basket.data:
ports:
- "6379:6379"
- "6379:6379" # Important: In a production environment your should remove the external port
rabbitmq:
ports:
- "15672:15672"
- "5672:5672"
- "15672:15672" # Important: In a production environment your should remove the external port
- "5672:5672" # Important: In a production environment your should remove the external port
mobileshoppingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5200:80"
volumes:
- ./src/ApiGateways/Mobile.Bff.Shopping/apigw:/app/configuration
mobilemarketingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5201:80"
volumes:
- ./src/ApiGateways/Mobile.Bff.Marketing/apigw:/app/configuration
webshoppingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5202:80"
volumes:
- ./src/ApiGateways/Web.Bff.Shopping/apigw:/app/configuration
webmarketingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5203:80"
volumes:
- ./src/ApiGateways/Web.Bff.Marketing/apigw:/app/configuration
mobileshoppingagg:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- urls__basket=http://basket.api
- urls__catalog=http://catalog.api
- urls__orders=http://ordering.api
- urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5120:80" # Important: In a production environment your should remove the external port (5120) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
webshoppingagg:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- urls__basket=http://basket.api
- urls__catalog=http://catalog.api
- urls__orders=http://ordering.api
- urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5121:80" # Important: In a production environment your should remove the external port (5121) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).

+ 88
- 48
docker-compose.prod.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
# The Production docker-compose file has to have the external/real IPs or DNS names for the services
# The ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP environment variable is taken, by default, from the ".env" file defined like:
@ -11,7 +11,7 @@ version: '3.3'
# Set ASPNETCORE_ENVIRONMENT= Development or Production, depending if you want to show up errors while testing.
#
# You need to start it with the following CLI command:
# docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
services:
@ -31,7 +31,7 @@ services:
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5103:80"
- "80" # The API Gateway redirects and access through the internal port (80).
catalog.api:
environment:
@ -50,7 +50,7 @@ services:
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5101:80"
- "80" # The API Gateway redirects and access through the internal port (80).
identity.api:
environment:
@ -64,11 +64,13 @@ services:
- MarketingApiClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5110
- BasketApiClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
- OrderingApiClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102
- MobileShoppingAggClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5120
- WebShoppingAggClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5121
- UseCustomizationData=True
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5105:80"
- "5105:80"
ordering.api:
environment:
@ -88,25 +90,7 @@ services:
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5102:80"
ordering.backgroundtasks:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- 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}
- EventBusUserName=${ESHOP_SERVICE_BUS_USERNAME}
- EventBusPassword=${ESHOP_SERVICE_BUS_PASSWORD}
- UseCustomizationData=True
- AzureServiceBusEnabled=False
- CheckUpdateTime=30000
- GracePeriodTime=1
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5111:80"
- "80" # The API Gateway redirects and access through the internal port (80).
marketing.api:
environment:
@ -130,21 +114,18 @@ services:
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5110:80"
- "80" # The API Gateway redirects and access through the internal port (80).
webspa:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103
- MarketingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5110
- LocationsUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5109
- PurchaseUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5202
- MarketingUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5203
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
@ -152,21 +133,18 @@ services:
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5104:80"
- "5104:80"
webmvc:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api
- BasketUrl=http://basket.api
- LocationsUrl=http://locations.api
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. #Remote: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
- MarketingUrl=http://marketing.api
- PurchaseUrl=http://webshoppingapigw
- IdentityUrl=http://10.0.75.1:5105 # Local Mac: Use http://docker.for.mac.localhost:5105 || Local Windows: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. || #Remote access: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
- MarketingUrl=http://webmarketingapigw
- CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
- BasketUrlHC=http://basket.api/hc
- MarketingUrlHC=http://marketing.api/hc
- PaymentUrlHC=http://payment.api/hc
@ -206,7 +184,7 @@ services:
- ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY}
- OrchestratorType=${ORCHESTRATOR_TYPE}
ports:
- "5108:80"
- "80" # The API Gateway redirects and access through the internal port (80).
locations.api:
environment:
@ -224,25 +202,87 @@ services:
- OrchestratorType=${ORCHESTRATOR_TYPE}
- UseLoadTest=${USE_LOADTEST:-False}
ports:
- "5109:80"
- "80" # The API Gateway redirects and access through the internal port (80).
sql.data:
environment:
- MSSQL_SA_PASSWORD=Pass@word
- SA_PASSWORD=Pass@word
- ACCEPT_EULA=Y
- MSSQL_PID=Developer
ports:
- "5433:1433"
- "5433:1433" # Important: In a production environment your should remove the external port
nosql.data:
ports:
- "27017:27017"
- "27017:27017" # Important: In a production environment your should remove the external port
basket.data:
ports:
- "6379:6379"
- "6379:6379" # Important: In a production environment your should remove the external port
rabbitmq:
ports:
- "15672:15672"
- "5672:5672"
- "15672:15672" # Important: In a production environment your should remove the external port
- "5672:5672" # Important: In a production environment your should remove the external port
mobileshoppingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5200:80" # Important: In a production environment your should remove the external port (5200) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
volumes:
- ./src/ApiGateways/Mobile.Bff.Shopping/apigw:/app/configuration
mobilemarketingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5201:80" # Important: In a production environment your should remove the external port (5201) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
volumes:
- ./src/ApiGateways/Mobile.Bff.Marketing/apigw:/app/configuration
webshoppingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5202:80" # Important: In a production environment your should remove the external port (5202) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
volumes:
- ./src/ApiGateways/Web.Bff.Shopping/apigw:/app/configuration
webmarketingapigw:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- IdentityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "5203:80" # Important: In a production environment your should remove the external port (5203) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
volumes:
- ./src/ApiGateways/Web.Bff.Marketing/apigw:/app/configuration
mobileshoppingagg:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- urls__basket=http://basket.api
- urls__catalog=http://catalog.api
- urls__orders=http://ordering.api
- urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "80" # Important: In a production environment your should remove the external port (5120) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).
webshoppingagg:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- urls__basket=http://basket.api
- urls__catalog=http://catalog.api
- urls__orders=http://ordering.api
- urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110.
ports:
- "80" # Important: In a production environment your should remove the external port (5121) kept here for microservice debugging purposes.
# The API Gateway redirects and access through the internal port (80).

+ 39
- 11
docker-compose.yml View File

@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
@ -38,15 +38,6 @@ services:
- sql.data
- rabbitmq
ordering.backgroundtasks:
image: eshop/ordering.backgroundtasks:${TAG:-latest}
build:
context: .
dockerfile: src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile
depends_on:
- sql.data
- rabbitmq
marketing.api:
image: eshop/marketing.api:${TAG:-latest}
build:
@ -115,4 +106,41 @@ services:
image: redis:alpine
rabbitmq:
image: rabbitmq:3-management-alpine
image: rabbitmq:3-management-alpine
mobileshoppingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
mobilemarketingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
webshoppingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
webmarketingapigw:
image: eshop/ocelotapigw:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/ApiGw-Base/Dockerfile
mobileshoppingagg:
image: eshop/mobileshoppingagg:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile
webshoppingagg:
image: eshop/webshoppingagg:${TAG:-latest}
build:
context: .
dockerfile: src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile

BIN
docs/Azure Dev eBook_Booklet.pdf View File


BIN
docs/MicrosoftAzure_StartGuide_Developers.pdf View File


+ 184
- 51
eShopOnContainers-ServicesAndWebApps.sln View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2024
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{FEA0C318-FFED-4D39-8781-265718CA43DD}"
EndProject
@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3AF739CD-81D8-428D-A08A-0A58372DEBF6}"
ProjectSection(SolutionItems) = preProject
.env = .env
Local.testsettings = Local.testsettings
NuGet.config = NuGet.config
EndProjectSection
@ -97,7 +98,35 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebHost", "WebHost", "{1815
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebHost.Customization", "src\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj", "{15F4B3AA-89B6-4A0D-9051-414305974781}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ordering.BackgroundTasks", "src\Services\Ordering\Ordering.BackgroundTasks\Ordering.BackgroundTasks.csproj", "{2FF56999-0266-48B2-ACC1-FEBC482A5105}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGateways", "ApiGateways", "{77849D35-37D4-4802-81DC-9477B2775A40}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGw-Base", "ApiGw-Base", "{EC91ADE9-3D66-4AB2-9FB4-2B585E1F3531}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile.Bff.Marketing", "Mobile.Bff.Marketing", "{DB813A36-11BA-41FE-B258-CA9A7152247B}"
ProjectSection(SolutionItems) = preProject
src\ApiGateways\Mobile.Bff.Marketing\apigw\configuration.json = src\ApiGateways\Mobile.Bff.Marketing\apigw\configuration.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile.Bff.Shopping", "Mobile.Bff.Shopping", "{0189E4FB-6E2B-4F2E-9B1D-5473D23FC6DB}"
ProjectSection(SolutionItems) = preProject
src\ApiGateways\Mobile.Bff.Shopping\apigw\configuration.json = src\ApiGateways\Mobile.Bff.Shopping\apigw\configuration.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web.Bff.Marketing", "Web.Bff.Marketing", "{F8F0921C-EE5D-4AED-A4D6-5BF5FAE02CB5}"
ProjectSection(SolutionItems) = preProject
src\ApiGateways\Web.Bff.Marketing\apigw\configuration.json = src\ApiGateways\Web.Bff.Marketing\apigw\configuration.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web.Bff.Shopping", "Web.Bff.Shopping", "{28C0F5C8-4849-4035-80AB-45639424E73F}"
ProjectSection(SolutionItems) = preProject
src\ApiGateways\Web.Bff.Shopping\apigw\configuration.json = src\ApiGateways\Web.Bff.Shopping\apigw\configuration.json
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OcelotApiGw", "src\ApiGateways\ApiGw-Base\OcelotApiGw.csproj", "{3F79558C-485D-49E1-BD3E-E12538D3D308}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mobile.Shopping.HttpAggregator", "src\ApiGateways\Mobile.Bff.Shopping\aggregator\Mobile.Shopping.HttpAggregator.csproj", "{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web.Shopping.HttpAggregator", "src\ApiGateways\Web.Bff.Shopping\aggregator\Web.Shopping.HttpAggregator.csproj", "{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -1313,54 +1342,150 @@ Global
{15F4B3AA-89B6-4A0D-9051-414305974781}.Release|x64.Build.0 = Release|Any CPU
{15F4B3AA-89B6-4A0D-9051-414305974781}.Release|x86.ActiveCfg = Release|Any CPU
{15F4B3AA-89B6-4A0D-9051-414305974781}.Release|x86.Build.0 = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|ARM.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhone.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x64.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x64.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x86.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.AppStore|x86.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|ARM.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|ARM.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhone.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x64.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x64.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x86.ActiveCfg = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Debug|x86.Build.0 = Debug|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|Any CPU.Build.0 = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|ARM.ActiveCfg = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|ARM.Build.0 = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhone.ActiveCfg = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhone.Build.0 = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x64.ActiveCfg = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x64.Build.0 = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x86.ActiveCfg = Release|Any CPU
{2FF56999-0266-48B2-ACC1-FEBC482A5105}.Release|x86.Build.0 = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|ARM.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|iPhone.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|x64.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|x64.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|x86.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.AppStore|x86.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|ARM.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|ARM.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|iPhone.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|x64.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|x64.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|x86.ActiveCfg = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Debug|x86.Build.0 = Debug|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|Any CPU.Build.0 = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|ARM.ActiveCfg = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|ARM.Build.0 = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|iPhone.ActiveCfg = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|iPhone.Build.0 = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|x64.ActiveCfg = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|x64.Build.0 = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|x86.ActiveCfg = Release|Any CPU
{3F79558C-485D-49E1-BD3E-E12538D3D308}.Release|x86.Build.0 = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|ARM.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|iPhone.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|x64.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|x64.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|x86.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.AppStore|x86.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|ARM.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|ARM.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|iPhone.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|x64.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|x64.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|x86.ActiveCfg = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Debug|x86.Build.0 = Debug|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|Any CPU.Build.0 = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|ARM.ActiveCfg = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|ARM.Build.0 = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|iPhone.ActiveCfg = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|iPhone.Build.0 = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|x64.ActiveCfg = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|x64.Build.0 = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|x86.ActiveCfg = Release|Any CPU
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0}.Release|x86.Build.0 = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|ARM.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|iPhone.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|x64.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|x64.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|x86.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.AppStore|x86.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|ARM.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|ARM.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|iPhone.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|x64.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|x64.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|x86.ActiveCfg = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Debug|x86.Build.0 = Debug|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|Any CPU.Build.0 = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|ARM.ActiveCfg = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|ARM.Build.0 = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|iPhone.ActiveCfg = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|iPhone.Build.0 = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|x64.ActiveCfg = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|x64.Build.0 = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|x86.ActiveCfg = Release|Any CPU
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1407,7 +1532,15 @@ Global
{969E793C-C413-490E-9C9D-B2B46DA5AF32} = {EF0337F2-ED00-4643-89FD-EE10863F1870}
{1815B651-941C-466B-AE33-D1D7EEB8F77F} = {DB0EFB20-B024-4E5E-A75C-52143C131D25}
{15F4B3AA-89B6-4A0D-9051-414305974781} = {1815B651-941C-466B-AE33-D1D7EEB8F77F}
{2FF56999-0266-48B2-ACC1-FEBC482A5105} = {0BD0DB92-2D98-44D9-9AC0-C59186D59B0B}
{77849D35-37D4-4802-81DC-9477B2775A40} = {932D8224-11F6-4D07-B109-DA28AD288A63}
{EC91ADE9-3D66-4AB2-9FB4-2B585E1F3531} = {77849D35-37D4-4802-81DC-9477B2775A40}
{DB813A36-11BA-41FE-B258-CA9A7152247B} = {77849D35-37D4-4802-81DC-9477B2775A40}
{0189E4FB-6E2B-4F2E-9B1D-5473D23FC6DB} = {77849D35-37D4-4802-81DC-9477B2775A40}
{F8F0921C-EE5D-4AED-A4D6-5BF5FAE02CB5} = {77849D35-37D4-4802-81DC-9477B2775A40}
{28C0F5C8-4849-4035-80AB-45639424E73F} = {77849D35-37D4-4802-81DC-9477B2775A40}
{3F79558C-485D-49E1-BD3E-E12538D3D308} = {EC91ADE9-3D66-4AB2-9FB4-2B585E1F3531}
{BEA37D6D-4CF2-4AE8-9575-72388E54FBD0} = {0189E4FB-6E2B-4F2E-9B1D-5473D23FC6DB}
{AF0828DB-8BDD-411A-AEEF-B780FBB8D8C1} = {28C0F5C8-4849-4035-80AB-45639424E73F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9}


+ 7
- 7
k8s/conf_cloud.yml View File

@ -3,14 +3,12 @@ kind: ConfigMap
metadata:
name: externalcfg
labels:
app: eshop
app: eshop
data:
# Basket.API entries
BasketBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
BasketRedisConStr: REDIS CONNECTION STRING FOR BASKET
basket__ConnectionString: REDIS CONNECTION STRING FOR BASKET
# Catalog.API entries
CatalogBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
CatalogSqlDb: Catalog SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
catalog__ConnectionString: Catalog SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
# Identity.API entries
IdentitySqlDb: Identity SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
# Locations.API entries
@ -30,5 +28,7 @@ data:
# Payment.API entries
PaymentBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
# Global entries
UseAzureServiceBus: "TRUE" IF USE AZURE SB ("FALSE" FOR USING RABBITMQ)
keystore: REDIS CONNECTION STRING FOR KEYSTORE
all_UseAzureServiceBus: "TRUE" IF USE AZURE SB ("FALSE" FOR USING RABBITMQ)
keystore: REDIS CONNECTION STRING FOR KEYSTORE
all_EventBusConnection: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX in case of using Azure)
all_InstrumentationKey: APPINSIGHTS KEY

+ 16
- 21
k8s/conf_local.yml View File

@ -5,28 +5,23 @@ metadata:
labels:
app: eshop
data:
BasketBus: rabbitmq
BasketRedisConStr: basket-data
CatalogBus: rabbitmq
CatalogSqlDb: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word;
CatalogAzureStorageEnabled: "False"
IdentitySqlDb: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;
LocationsBus: rabbitmq
LocationsNoSqlDb: mongodb://nosql-data
LocationsNoSqlDbName: LocationsDb
MarketingBus: rabbitmq
MarketingNoSqlDb: mongodb://nosql-data
MarketingNoSqlDbName: MarketingDb
MarketingSqlDb: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word;
OrderingBus: rabbitmq
OrderingSqlDb: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;
PaymentBus: rabbitmq
UseAzureServiceBus: "False"
EnableLoadTest: "False"
basket__ConnectionString: basket-data
catalog__ConnectionString: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word;
catalog__AzureStorageEnabled: "False"
identity__ConnectionString: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;
locations__ConnectionString: mongodb://nosql-data
locations__Database: LocationsDb
marketing__MongoConnectionString: mongodb://nosql-data
marketing__MongoDatabase: MarketingDb
marketing__ConnectionString: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word;
ordering__ConnectionString: Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;
keystore: keystore-data
GracePeriodManager_GracePeriodTime: "1"
GracePeriodManager_CheckUpdateTime: "15000"
Instrumentation_Key: ""
GracePeriodManager__GracePeriodTime: "1"
GracePeriodManager__CheckUpdateTime: "15000"
all__EventBusConnection: rabbitmq
all__InstrumentationKey: ""
all__EnableLoadTest: "False"
all__UseAzureServiceBus: "False"

+ 37
- 37
k8s/deploy.ps1 View File

@ -65,7 +65,7 @@ if ($buildImages) {
docker-compose -p .. -f ../docker-compose.yml build
Write-Host "Pushing images to $registry/$dockerOrg..." -ForegroundColor Yellow
$services = ("basket.api", "catalog.api", "identity.api", "ordering.api", "ordering.backgroundtasks", "marketing.api","payment.api","locations.api", "webmvc", "webspa", "webstatus")
$services = ("basket.api", "catalog.api", "identity.api", "ordering.api", "marketing.api","payment.api","locations.api", "webmvc", "webspa", "webstatus", "ocelotapigw", "mobileshoppingagg", "webshoppingagg")
foreach ($service in $services) {
$imageFqdn = if ($useDockerHub) {"$dockerOrg/${service}"} else {"$registry/$dockerOrg/${service}"}
@ -103,8 +103,10 @@ if (-not [string]::IsNullOrEmpty($dockerUser)) {
Write-Host "Removing existing services & deployments.." -ForegroundColor Yellow
ExecKube -cmd 'delete deployments --all'
ExecKube -cmd 'delete services --all'
ExecKube -cmd 'delete configmap internalurls'
ExecKube -cmd 'delete configmap urls'
ExecKube -cmd 'delete configmap externalcfg'
ExecKube -cmd 'delete configmap ocelot'
# start sql, rabbitmq, frontend deployments
if ($deployInfrastructure) {
@ -113,48 +115,35 @@ if ($deployInfrastructure) {
}
Write-Host 'Deploying ocelot APIGW' -ForegroundColor Yellow
ExecKube "create configmap ocelot --from-file=mm=ocelot/configuration-mobile-marketing.json --from-file=ms=ocelot/configuration-mobile-shopping.json --from-file=wm=ocelot/configuration-web-marketing.json --from-file=ws=ocelot/configuration-web-shopping.json "
ExecKube -cmd "apply -f ocelot/deployment.yaml"
ExecKube -cmd "apply -f ocelot/service.yaml"
Write-Host 'Deploying code deployments (Web APIs, Web apps, ...)' -ForegroundColor Yellow
ExecKube -cmd 'create -f services.yaml'
ExecKube -cmd 'create -f internalurls.yaml'
ExecKube -cmd 'create configmap urls `
--from-literal=BasketUrl=http://basket `
--from-literal=BasketHealthCheckUrl=http://basket/hc `
--from-literal=CatalogUrl=http://$($externalDns)/catalog-api `
--from-literal=CatalogHealthCheckUrl=http://catalog/hc `
--from-literal=PicBaseUrl=http://$($externalDns)/catalog-api/api/v1/catalog/items/[0]/pic/ `
--from-literal=Marketing_PicBaseUrl=http://$($externalDns)/marketing-api/api/v1/campaigns/[0]/pic/ `
--from-literal=IdentityUrl=http://$($externalDns)/identity `
--from-literal=IdentityHealthCheckUrl=http://identity/hc `
--from-literal=OrderingUrl=http://ordering `
--from-literal=OrderingHealthCheckUrl=http://ordering/hc `
--from-literal=MvcClientExternalUrl=http://$($externalDns)/webmvc `
--from-literal=WebMvcHealthCheckUrl=http://webmvc/hc `
--from-literal=MvcClientOrderingUrl=http://ordering `
--from-literal=MvcClientCatalogUrl=http://catalog `
--from-literal=MvcClientBasketUrl=http://basket `
--from-literal=MvcClientMarketingUrl=http://marketing `
--from-literal=MvcClientLocationsUrl=http://locations `
--from-literal=MarketingHealthCheckUrl=http://marketing/hc `
--from-literal=WebSpaHealthCheckUrl=http://webspa/hc `
--from-literal=SpaClientMarketingExternalUrl=http://$($externalDns)/marketing-api `
--from-literal=SpaClientOrderingExternalUrl=http://$($externalDns)/ordering-api `
--from-literal=SpaClientCatalogExternalUrl=http://$($externalDns)/catalog-api `
--from-literal=SpaClientBasketExternalUrl=http://$($externalDns)/basket-api `
--from-literal=SpaClientIdentityExternalUrl=http://$($externalDns)/identity `
--from-literal=SpaClientLocationsUrl=http://$($externalDns)/locations-api `
--from-literal=LocationsHealthCheckUrl=http://locations/hc `
--from-literal=SpaClientExternalUrl=http://$($externalDns) `
--from-literal=LocationApiClient=http://$($externalDns)/locations-api `
--from-literal=MarketingApiClient=http://$($externalDns)/marketing-api `
--from-literal=BasketApiClient=http://$($externalDns)/basket-api `
--from-literal=OrderingApiClient=http://$($externalDns)/ordering-api `
--from-literal=PaymentHealthCheckUrl=http://payment/hc'
--from-literal=PicBaseUrl=http://$($externalDns)/webshoppingapigw/api/v1/c/catalog/items/[0]/pic/ `
--from-literal=Marketing_PicBaseUrl=http://$($externalDns)/webmarketingapigw/api/v1/m/campaigns/[0]/pic/ `
--from-literal=mvc_e=http://$($externalDns)/webmvc `
--from-literal=marketingapigw_e=http://$($externalDns)/webmarketingapigw `
--from-literal=webshoppingapigw_e=http://$($externalDns)/webshoppingapigw `
--from-literal=mobileshoppingagg_e=http://$($externalDns)/mobileshoppingagg `
--from-literal=webshoppingagg_e=http://$($externalDns)/webshoppingagg `
--from-literal=identity_e=http://$($externalDns)/identity `
--from-literal=spa_e=http://$($externalDns) `
--from-literal=locations_e=http://$($externalDns)/locations-api `
--from-literal=marketing_e=http://$($externalDns)/marketing-api `
--from-literal=basket_e=http://$($externalDns)/basket-api `
--from-literal=ordering_e=http://$($externalDns)/ordering-api `
--from-literal=xamarin_callback_e=http://$($externalDns)/xamarincallback'
ExecKube -cmd 'label configmap urls app=eshop'
Write-Host "Deploying configuration from $configFile" -ForegroundColor Yellow
ExecKube -cmd "create -f $configFile"
Write-Host "Creating deployments..." -ForegroundColor Yellow
@ -178,8 +167,14 @@ ExecKube -cmd 'set image deployments/payment payment=${registryPath}${dockerOrg}
ExecKube -cmd 'set image deployments/webmvc webmvc=${registryPath}${dockerOrg}/webmvc:$imageTag'
ExecKube -cmd 'set image deployments/webstatus webstatus=${registryPath}${dockerOrg}/webstatus:$imageTag'
ExecKube -cmd 'set image deployments/webspa webspa=${registryPath}${dockerOrg}/webspa:$imageTag'
ExecKube -cmd 'set image deployments/orderingbackground orderingbackground=${registryPath}${dockerOrg}/ordering.backgroundtasks:$imageTag'
ExecKube -cmd 'set image deployments/mobileshoppingagg mobileshoppingagg=${registryPath}${dockerOrg}/mobileshoppingagg:$imageTag'
ExecKube -cmd 'set image deployments/webshoppingagg webshoppingagg=${registryPath}${dockerOrg}/webshoppingagg:$imageTag'
ExecKube -cmd 'set image deployments/apigwmm apigwmm=${registryPath}${dockerOrg}/ocelotapigw:$imageTag'
ExecKube -cmd 'set image deployments/apigwms apigwms=${registryPath}${dockerOrg}/ocelotapigw:$imageTag'
ExecKube -cmd 'set image deployments/apigwwm apigwwm=${registryPath}${dockerOrg}/ocelotapigw:$imageTag'
ExecKube -cmd 'set image deployments/apigwws apigwws=${registryPath}${dockerOrg}/ocelotapigw:$imageTag'
Write-Host "Execute rollout..." -ForegroundColor Yellow
ExecKube -cmd 'rollout resume deployments/basket'
@ -192,7 +187,12 @@ ExecKube -cmd 'rollout resume deployments/payment'
ExecKube -cmd 'rollout resume deployments/webmvc'
ExecKube -cmd 'rollout resume deployments/webstatus'
ExecKube -cmd 'rollout resume deployments/webspa'
ExecKube -cmd 'rollout resume deployments/orderingbackground'
ExecKube -cmd 'rollout resume deployments/mobileshoppingagg'
ExecKube -cmd 'rollout resume deployments/webshoppingagg'
ExecKube -cmd 'rollout resume deployments/apigwmm'
ExecKube -cmd 'rollout resume deployments/apigwms'
ExecKube -cmd 'rollout resume deployments/apigwwm'
ExecKube -cmd 'rollout resume deployments/apigwws'
Write-Host "WebSPA is exposed at http://$externalDns, WebMVC at http://$externalDns/webmvc, WebStatus at http://$externalDns/webstatus" -ForegroundColor Yellow

+ 228
- 282
k8s/deployments.yaml View File

@ -21,32 +21,32 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: BasketRedisConStr
key: basket__ConnectionString
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: BasketBus
key: all__EventBusConnection
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
key: all__UseAzureServiceBus
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
key: identity_e
- name: UseLoadTest
valueFrom:
configMapKeyRef:
name: externalcfg
key: EnableLoadTest
key: all__EnableLoadTest
- name: OrchestratorType
value: 'K8S'
ports:
@ -56,15 +56,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -91,7 +84,7 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: CatalogSqlDb
key: catalog__ConnectionString
- name: PicBaseUrl
valueFrom:
configMapKeyRef:
@ -101,17 +94,17 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: CatalogAzureStorageEnabled
key: catalog__AzureStorageEnabled
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: CatalogBus
key: all__EventBusConnection
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: OrchestratorType
value: 'K8S'
ports:
@ -121,15 +114,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -156,7 +142,7 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: IdentitySqlDb
key: identity__ConnectionString
- name: DPConnectionString
valueFrom:
configMapKeyRef:
@ -168,37 +154,52 @@ spec:
valueFrom:
configMapKeyRef:
name: urls
key: MvcClientExternalUrl
key: mvc_e
- name: SpaClient
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientExternalUrl
key: spa_e
- name: LocationApiClient
valueFrom:
configMapKeyRef:
name: urls
key: LocationApiClient
key: locations_e
- name: MarketingApiClient
valueFrom:
configMapKeyRef:
name: urls
key: MarketingApiClient
key: marketing_e
- name: BasketApiClient
valueFrom:
configMapKeyRef:
name: urls
key: BasketApiClient
key: basket_e
- name: OrderingApiClient
valueFrom:
configMapKeyRef:
name: urls
key: OrderingApiClient
key: ordering_e
- name: MobileShoppingAggClient
valueFrom:
configMapKeyRef:
name: urls
key: mobileshoppingagg_e
- name: WebShoppingAggClient
valueFrom:
configMapKeyRef:
name: urls
key: webshoppingagg_e
- name: XamarinCallback
valueFrom:
configMapKeyRef:
name: urls
key: xamarin_callback_e
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: OrchestratorType
value: 'K8S'
ports:
@ -208,15 +209,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -243,107 +237,42 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: OrderingSqlDb
key: ordering__ConnectionString
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: OrderingBus
key: all__EventBusConnection
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
key: all__UseAzureServiceBus
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
- name: UseLoadTest
valueFrom:
configMapKeyRef:
name: externalcfg
key: EnableLoadTest
- name: OrchestratorType
value: 'K8S'
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: orderingbackground
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: orderingbackground
spec:
containers:
- name: orderingbackground
image: eshop/ordering.backgroundtasks
imagePullPolicy: Always
env:
- name: PATH_BASE
value: /ordering-backgroundtasks
- name: ConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: OrderingSqlDb
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: OrderingBus
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
key: identity_e
- name: CheckUpdateTime
valueFrom:
configMapKeyRef:
name: externalcfg
key: GracePeriodManager_CheckUpdateTime
key: GracePeriodManager__CheckUpdateTime
- name: GracePeriodTime
valueFrom:
configMapKeyRef:
name: externalcfg
key: GracePeriodManager_GracePeriodTime
key: GracePeriodManager__GracePeriodTime
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: UseLoadTest
valueFrom:
configMapKeyRef:
name: externalcfg
key: EnableLoadTest
key: all__EnableLoadTest
- name: OrchestratorType
value: 'K8S'
ports:
@ -353,15 +282,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -388,42 +310,42 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: LocationsNoSqlDb
key: locations__ConnectionString
- name: Database
valueFrom:
configMapKeyRef:
name: externalcfg
key: LocationsNoSqlDbName
key: locations__Database
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
key: all__UseAzureServiceBus
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: LocationsBus
key: all__EventBusConnection
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
name: internalurls
key: identity
- name: IdentityUrlExternal
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
key: identity_e
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: UseLoadTest
valueFrom:
configMapKeyRef:
name: externalcfg
key: EnableLoadTest
key: all__EnableLoadTest
- name: OrchestratorType
value: 'K8S'
ports:
@ -433,15 +355,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 50
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -468,37 +383,37 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingSqlDb
key: marketing__ConnectionString
- name: MongoConnectionString
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingNoSqlDb
key: marketing__MongoConnectionString
- name: MongoDatabase
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingNoSqlDbName
key: marketing__MongoDatabase
- name: AzureServiceBusEnabled
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
key: all__UseAzureServiceBus
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: MarketingBus
key: all__EventBusConnection
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
name: internalurls
key: identity
- name: IdentityUrlExternal
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
key: identity_e
- name: PicBaseUrl
valueFrom:
configMapKeyRef:
@ -508,12 +423,12 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: UseLoadTest
valueFrom:
configMapKeyRef:
name: externalcfg
key: EnableLoadTest
key: all__EnableLoadTest
- name: OrchestratorType
value: 'K8S'
ports:
@ -523,15 +438,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -558,17 +466,17 @@ spec:
valueFrom:
configMapKeyRef:
name: externalcfg
key: UseAzureServiceBus
key: all__UseAzureServiceBus
- name: EventBusConnection
valueFrom:
configMapKeyRef:
name: externalcfg
key: PaymentBus
key: all__EventBusConnection
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: OrchestratorType
value: 'K8S'
ports:
@ -578,15 +486,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -616,81 +517,66 @@ spec:
key: keystore
- name: IsClusterEnv
value: 'True'
- name: BasketUrl
- name: PurchaseUrl
valueFrom:
configMapKeyRef:
name: urls
key: MvcClientBasketUrl
name: internalurls
key: apigwws
- name: CallBackUrl
valueFrom:
configMapKeyRef:
name: urls
key: MvcClientExternalUrl
- name: LocationsUrl
valueFrom:
configMapKeyRef:
name: urls
key: MvcClientLocationsUrl
- name: CatalogUrl
valueFrom:
configMapKeyRef:
name: urls
key: MvcClientCatalogUrl
key: mvc_e
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityUrl
- name: OrderingUrl
valueFrom:
configMapKeyRef:
name: urls
key: MvcClientOrderingUrl
key: identity_e
- name: MarketingUrl
valueFrom:
configMapKeyRef:
name: urls
key: MvcClientMarketingUrl
name: internalurls
key: apigwwm
- name: BasketUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: BasketHealthCheckUrl
name: internalurls
key: basket__hc
- name: CatalogUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: CatalogHealthCheckUrl
name: internalurls
key: catalog__hc
- name: IdentityUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: IdentityHealthCheckUrl
name: internalurls
key: identity__hc
- name: OrderingUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: OrderingHealthCheckUrl
name: internalurls
key: ordering__hc
- name: MarketingUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: MarketingHealthCheckUrl
name: internalurls
key: marketing__hc
- name: PaymentUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: PaymentHealthCheckUrl
name: internalurls
key: payment__hc
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: UseLoadTest
valueFrom:
configMapKeyRef:
name: externalcfg
key: EnableLoadTest
key: all__EnableLoadTest
- name: OrchestratorType
value: 'K8S'
ports:
@ -700,15 +586,8 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
@ -734,53 +613,53 @@ spec:
- name: BasketUrl
valueFrom:
configMapKeyRef:
name: urls
key: BasketHealthCheckUrl
name: internalurls
key: basket__hc
- name: CatalogUrl
valueFrom:
configMapKeyRef:
name: urls
key: CatalogHealthCheckUrl
name: internalurls
key: catalog__hc
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: IdentityHealthCheckUrl
name: internalurls
key: identity__hc
- name: OrderingUrl
valueFrom:
configMapKeyRef:
name: urls
key: OrderingHealthCheckUrl
name: internalurls
key: ordering__hc
- name: LocationsUrl
valueFrom:
configMapKeyRef:
name: urls
key: LocationsHealthCheckUrl
name: internalurls
key: locations__hc
- name: MarketingUrl
valueFrom:
configMapKeyRef:
name: urls
key: MarketingHealthCheckUrl
name: internalurls
key: marketing__hc
- name: mvc
valueFrom:
configMapKeyRef:
name: urls
key: WebMvcHealthCheckUrl
name: internalurls
key: mvc__hc
- name: spa
valueFrom:
configMapKeyRef:
name: urls
key: WebSpaHealthCheckUrl
name: internalurls
key: spa__hc
- name: PaymentUrl
valueFrom:
configMapKeyRef:
name: urls
key: PaymentHealthCheckUrl
name: internalurls
key: payment__hc
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: OrchestratorType
value: 'K8S'
ports:
@ -814,76 +693,61 @@ spec:
key: keystore
- name: IsClusterEnv
value: 'True'
- name: BasketUrl
- name: PurchaseUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientBasketExternalUrl
key: webshoppingapigw_e
- name: CallBackUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientExternalUrl
- name: CatalogUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientCatalogExternalUrl
key: spa_e
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientIdentityExternalUrl
- name: OrderingUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientOrderingExternalUrl
key: identity_e
- name: MarketingUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientMarketingExternalUrl
- name: LocationsUrl
valueFrom:
configMapKeyRef:
name: urls
key: SpaClientLocationsUrl
key: marketingapigw_e
- name: BasketUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: BasketHealthCheckUrl
name: internalurls
key: basket__hc
- name: CatalogUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: CatalogHealthCheckUrl
name: internalurls
key: catalog__hc
- name: IdentityUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: IdentityHealthCheckUrl
name: internalurls
key: identity__hc
- name: OrderingUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: OrderingHealthCheckUrl
name: internalurls
key: ordering__hc
- name: MarketingUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: MarketingHealthCheckUrl
name: internalurls
key: marketing__hc
- name: PaymentUrlHC
valueFrom:
configMapKeyRef:
name: urls
key: PaymentHealthCheckUrl
name: internalurls
key: payment__hc
- name: ApplicationInsights__InstrumentationKey
valueFrom:
configMapKeyRef:
name: externalcfg
key: Instrumentation_Key
key: all__InstrumentationKey
- name: OrchestratorType
value: 'K8S'
ports:
@ -893,14 +757,96 @@ spec:
path: /hc
port: 80
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 60
livenessProbe:
httpGet:
path: /liveness
port: 80
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 60
initialDelaySeconds: 300
periodSeconds: 240
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: webshoppingagg
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: webshoppingagg
spec:
containers:
- name: webshoppingagg
image: eshop/webshoppingagg
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80
- name: urls__basket
valueFrom:
configMapKeyRef:
name: internalurls
key: basket
- name: urls__catalog
valueFrom:
configMapKeyRef:
name: internalurls
key: catalog
- name: urls__orders
valueFrom:
configMapKeyRef:
name: internalurls
key: ordering
- name: urls__identity
valueFrom:
configMapKeyRef:
name: internalurls
key: identity
ports:
- containerPort: 80
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mobileshoppingagg
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: mobileshoppingagg
spec:
containers:
- name: mobileshoppingagg
image: eshop/mobileshoppingagg
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: http://0.0.0.0:80
- name: urls__basket
valueFrom:
configMapKeyRef:
name: internalurls
key: basket
- name: urls__catalog
valueFrom:
configMapKeyRef:
name: internalurls
key: catalog
- name: urls__orders
valueFrom:
configMapKeyRef:
name: internalurls
key: ordering
- name: urls__identity
valueFrom:
configMapKeyRef:
name: internalurls
key: identity
ports:
- containerPort: 80
imagePullSecrets:
- name: registry-key
---

+ 0
- 47
k8s/frontend.yaml View File

@ -1,47 +0,0 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: frontend
name: frontend
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: eshop
component: frontend
type: LoadBalancer
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
template:
metadata:
labels:
app: eshop
component: frontend
spec:
containers:
- name: nginx
image: nginx:1.13.8-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
lifecycle:
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
volumeMounts:
- name: config
mountPath: /etc/nginx
volumes:
- name: config
configMap:
name: config-files
items:
- key: nginx-conf
path: nginx.conf

+ 5
- 7
k8s/gen-k8s-env-aks.ps1 View File

@ -3,11 +3,9 @@
[parameter(Mandatory=$true)][string]$location,
[parameter(Mandatory=$false)][string]$registryName,
[parameter(Mandatory=$true)][string]$serviceName,
[parameter(Mandatory=$true)][string]$dnsName,
[parameter(Mandatory=$true)][string]$createAcr=$true,
[parameter(Mandatory=$false)][int]$nodeCount=2,
[parameter(Mandatory=$false)][string]$nodeVMSize="Standard_D2_v2",
[parameter(Mandatory=$false)][string]$kubernetesVersion="1.7.7"
[parameter(Mandatory=$false)][int]$nodeCount=3,
[parameter(Mandatory=$false)][string]$nodeVMSize="Standard_D2_v2"
)
# Create resource group
@ -22,12 +20,12 @@ if ($createAcr -eq $true) {
# Create kubernetes orchestrator
Write-Host "Creating kubernetes orchestrator..." -ForegroundColor Yellow
az aks create --resource-group=$resourceGroupName --name=$serviceName --dns-name-prefix=$dnsName --generate-ssh-keys --node-count=$nodeCount --node-vm-size=$nodeVMSize --kubernetes-version $kubernetesVersion
az aks create --resource-group=$resourceGroupName --name=$serviceName --generate-ssh-keys --node-count=$nodeCount --node-vm-size=$nodeVMSize
# Retrieve kubernetes cluster configuration and save it under ~/.kube/config
# Retrieve kubernetes cluster configuration and save it under ~/.kube/config
az aks get-credentials --resource-group=$resourceGroupName --name=$serviceName
if ($createAcr -eq $true) {
# Show ACR credentials
az acr credential show -n $registryName
}
}

+ 23
- 19
k8s/ingress.yaml View File

@ -11,22 +11,10 @@ spec:
rules:
- http:
paths:
- path: /basket-api
backend:
serviceName: basket
servicePort: 80
- path: /catalog-api
backend:
serviceName: catalog
servicePort: 80
- path: /identity
backend:
serviceName: identity
servicePort: 80
- path: /ordering-api
backend:
serviceName: ordering
servicePort: 80
- path: /webmvc
backend:
serviceName: webmvc
@ -35,21 +23,37 @@ spec:
backend:
serviceName: webstatus
servicePort: 80
- path: /marketing-api
- path: /webshoppingapigw
backend:
serviceName: ocelotapigw-ws
servicePort: 80
- path: /webmarketingapigw
backend:
serviceName: marketing
serviceName: ocelotapigw-wm
servicePort: 80
- path: /payment-api
- path: /mobilemarketingapigw
backend:
serviceName: payment
serviceName: ocelotapigw-mm
servicePort: 80
- path: /mobileshoppingapigw
backend:
serviceName: ocelotapigw-ms
servicePort: 80
- path: /webshoppingagg
backend:
serviceName: webshoppingagg
servicePort: 80
- path: /locations-api
- path: /mobileshoppingagg
backend:
serviceName: locations
serviceName: mobileshoppingagg
servicePort: 80
- path: /payment-api
backend:
serviceName: payment
servicePort: 80
- path: /
backend:
serviceName: webspa
servicePort: 80

+ 31
- 0
k8s/internalurls.yaml View File

@ -0,0 +1,31 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: internalurls
labels:
app: eshop
data:
# Internal Services & healthchecks
basket: http://basket
basket__hc: http://basket/hc
catalog: http://catalog
catalog__hc: http://catalog/hc
identity: http://identity
identity__hc: http://identity/hc
ordering: http://ordering
ordering__hc: http://ordering/hc
marketing: http://marketing
marketing__hc: http://marketing/hc
locations: http://locations
locations__hc: http://locations/hc
payment__hc: http://payment/hc
mvc__hc: http://webmvc/hc
spa__hc: http://webspa/hc
# Aggreggators
mobileshoppingagg: http://mobileshoppingagg
webshoppingagg: http://webshoppingagg
# API GWs
apigwmm: http://ocelotapigw-mm
apigwms: http://ocelotapigw-ms
apigwwm: http://ocelotapigw-wm
apigwws: http://ocelotapigw-ws

+ 34
- 0
k8s/ocelot/configuration-mobile-marketing.json View File

@ -0,0 +1,34 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/m/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/l/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 142
- 0
k8s/ocelot/configuration-mobile-shopping.json View File

@ -0,0 +1,142 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/c/{everything}",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/b/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/o/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "mobileshoppingagg",
"Port": 80
}
],
"UpstreamPathTemplate": "/{everything}",
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering",
"Port": 80
}
],
"UpstreamPathTemplate": "/orders-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket",
"Port": 80
}
],
"UpstreamPathTemplate": "/basket-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog",
"Port": 80
}
],
"UpstreamPathTemplate": "/catalog-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing",
"Port": 80
}
],
"UpstreamPathTemplate": "/marketing-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "payment",
"Port": 80
}
],
"UpstreamPathTemplate": "/payment-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/location-api/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 34
- 0
k8s/ocelot/configuration-web-marketing.json View File

@ -0,0 +1,34 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/m/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/l/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 142
- 0
k8s/ocelot/configuration-web-shopping.json View File

@ -0,0 +1,142 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/c/{everything}",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/b/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/o/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "webshoppingagg",
"Port": 80
}
],
"UpstreamPathTemplate": "/{everything}",
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering",
"Port": 80
}
],
"UpstreamPathTemplate": "/orders-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket",
"Port": 80
}
],
"UpstreamPathTemplate": "/basket-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog",
"Port": 80
}
],
"UpstreamPathTemplate": "/catalog-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing",
"Port": 80
}
],
"UpstreamPathTemplate": "/marketing-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "payment",
"Port": 80
}
],
"UpstreamPathTemplate": "/payment-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/location-api/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 155
- 0
k8s/ocelot/deployment.yaml View File

@ -0,0 +1,155 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: apigwmm
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: apigwmm
spec:
containers:
- name: apigwmm
image: eshop/ocelotapigw
imagePullPolicy: Always
env:
- name: PATH_BASE
value: /mobilemarketingapigw
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: internalurls
key: identity
ports:
- containerPort: 80
volumeMounts:
- name: config
mountPath: /app/configuration
volumes:
- name: config
configMap:
name: ocelot
items:
- key: mm
path: configuration.json
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: apigwms
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: apigwms
spec:
containers:
- name: apigwms
image: eshop/ocelotapigw
imagePullPolicy: Always
env:
- name: PATH_BASE
value: /mobileshoppingapigw
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: internalurls
key: identity
ports:
- containerPort: 80
volumeMounts:
- name: config
mountPath: /app/configuration
volumes:
- name: config
configMap:
name: ocelot
items:
- key: ms
path: configuration.json
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: apigwwm
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: apigwwm
spec:
containers:
- name: apigwwm
image: eshop/ocelotapigw
imagePullPolicy: Always
env:
- name: PATH_BASE
value: /webmarketingapigw
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: internalurls
key: identity
ports:
- containerPort: 80
volumeMounts:
- name: config
mountPath: /app/configuration
volumes:
- name: config
configMap:
name: ocelot
items:
- key: wm
path: configuration.json
imagePullSecrets:
- name: registry-key
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: apigwws
spec:
paused: true
template:
metadata:
labels:
app: eshop
component: apigwws
spec:
containers:
- name: apigwws
image: eshop/ocelotapigw
imagePullPolicy: Always
env:
- name: PATH_BASE
value: /webshoppingapigw
- name: IdentityUrl
valueFrom:
configMapKeyRef:
name: internalurls
key: identity
ports:
- containerPort: 80
volumeMounts:
- name: config
mountPath: /app/configuration
volumes:
- name: config
configMap:
name: ocelot
items:
- key: ws
path: configuration.json
imagePullSecrets:
- name: registry-key

+ 55
- 0
k8s/ocelot/service.yaml View File

@ -0,0 +1,55 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: ocelotapigw-mm
name: ocelotapigw-mm
spec:
ports:
- port: 80
selector:
app: eshop
component: apigwmm
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: ocelotapigw-ms
name: ocelotapigw-ms
spec:
ports:
- port: 80
selector:
app: eshop
component: apigwms
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: ocelotapigw-wm
name: ocelotapigw-wm
spec:
ports:
- port: 80
selector:
app: eshop
component: apigwwm
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: ocelotapigw-ws
name: ocelotapigw-ws
spec:
ports:
- port: 80
selector:
app: eshop
component: apigwws

+ 28
- 0
k8s/services.yaml View File

@ -112,6 +112,34 @@ spec:
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: webshoppingagg
name: webshoppingagg
spec:
ports:
- port: 80
selector:
app: eshop
component: webshoppingagg
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop
component: mobileshoppingagg
name: mobileshoppingagg
spec:
ports:
- port: 80
selector:
app: eshop
component: mobileshoppingagg
---
apiVersion: v1
kind: Service
metadata:
labels:
app: eshop


+ 19
- 0
src/ApiGateways/ApiGw-Base/Dockerfile View File

@ -0,0 +1,19 @@
FROM microsoft/aspnetcore:2.0 AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build
WORKDIR /src
COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/
RUN dotnet restore src/ApiGateways/ApiGw-Base/
COPY . .
WORKDIR /src/src/ApiGateways/ApiGw-Base/
RUN dotnet build -c Release -o /app
FROM build AS publish
RUN dotnet publish -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "OcelotApiGw.dll"]

+ 16
- 0
src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" />
<PackageReference Include="Ocelot" Version="3.0.0" />
</ItemGroup>
</Project>

+ 31
- 0
src/ApiGateways/ApiGw-Base/Program.cs View File

@ -0,0 +1,31 @@
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 Microsoft.Extensions.DependencyInjection;
namespace OcelotApiGw
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
var builder = WebHost.CreateDefaultBuilder(args);
builder.ConfigureServices(s => s.AddSingleton(builder))
.ConfigureAppConfiguration(ic => ic.AddJsonFile(Path.Combine("configuration", "configuration.json")))
.UseStartup<Startup>();
var host = builder.Build();
return host;
}
}
}

+ 27
- 0
src/ApiGateways/ApiGw-Base/Properties/launchSettings.json View File

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56755/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"OcelotApiGw": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:64021/"
}
}
}

+ 91
- 0
src/ApiGateways/ApiGw-Base/Startup.cs View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CacheManager.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace OcelotApiGw
{
public class Startup
{
private readonly IConfiguration _cfg;
public Startup(IConfiguration configuration)
{
_cfg = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
var identityUrl = _cfg.GetValue<string>("IdentityUrl");
var authenticationProviderKey = "IdentityApiKey";
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, x =>
{
x.Authority = identityUrl;
x.RequireHttpsMetadata = false;
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidAudiences = new[] { "orders", "basket", "locations", "marketing", "mobileshoppingagg", "webshoppingagg" }
};
x.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents()
{
OnAuthenticationFailed = async ctx =>
{
int i = 0;
},
OnTokenValidated = async ctx =>
{
int i = 0;
},
OnMessageReceived = async ctx =>
{
int i = 0;
}
};
});
services.AddOcelot(_cfg);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var pathBase = _cfg["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
loggerFactory.AddConsole(_cfg.GetSection("Logging"));
app.UseCors("CorsPolicy");
app.UseOcelot().Wait();
}
}
}

+ 10
- 0
src/ApiGateways/ApiGw-Base/appsettings.json View File

@ -0,0 +1,10 @@
{
"Logging": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Trace",
"System": "Information",
"Microsoft": "Information"
}
}
}

+ 34
- 0
src/ApiGateways/Mobile.Bff.Marketing/apigw/configuration.json View File

@ -0,0 +1,34 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/m/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/l/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 31
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs View File

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

+ 133
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs View File

@ -0,0 +1,133 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class BasketController : Controller
{
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
public BasketController(ICatalogService catalogService, IBasketService basketService)
{
_catalog = catalogService;
_basket = basketService;
}
[HttpPost]
[HttpPut]
public async Task<IActionResult> UpdateAllBasket([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BuyerId);
if (currentBasket == null)
{
currentBasket = new BasketData(data.BuyerId);
}
var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId));
var newBasket = new BasketData(data.BuyerId);
foreach (var bitem in data.Items)
{
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
{
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
}
newBasket.Items.Add(new BasketDataItem()
{
Id = bitem.Id,
ProductId = catalogItem.Id.ToString(),
ProductName = catalogItem.Name,
PictureUrl = catalogItem.PictureUri,
UnitPrice = catalogItem.Price,
Quantity = bitem.Quantity
});
}
await _basket.Update(newBasket);
return Ok(newBasket);
}
[HttpPut]
[Route("items")]
public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
return BadRequest("No updates sent");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
}
// Update with new quantities
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
await _basket.Update(currentBasket);
return Ok(currentBasket);
}
[HttpPost]
[Route("items")]
public async Task<IActionResult> AddBasketItem([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
return BadRequest("Invalid payload");
}
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItem(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id.ToString(),
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
// Step 4: Update basket
await _basket.Update(currentBasket);
return Ok();
}
}
}

+ 18
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs View File

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

+ 42
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs View File

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class OrderController : Controller
{
private readonly IBasketService _basketService;
private readonly IOrderApiClient _orderClient;
public OrderController(IBasketService basketService, IOrderApiClient orderClient)
{
_basketService = basketService;
_orderClient = orderClient;
}
[Route("draft/{basketId}")]
[HttpGet]
public async Task<IActionResult> GetOrderDraft(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket);
return Ok(orderDraft);
}
}
}

+ 19
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile View File

@ -0,0 +1,19 @@
FROM microsoft/aspnetcore:2.0.5 AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator
RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish
RUN dotnet publish --no-restore -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Mobile.Shopping.HttpAggregator.dll"]

+ 33
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs View File

@ -0,0 +1,33 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters
{
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
namespace Basket.API.Infrastructure.Filters
{
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 [] { "Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator" } }
});
}
}
}
}
}

+ 27
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Mobile.Shopping.HttpAggregator</AssemblyName>
<RootNamespace>Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator</RootNamespace>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
</ItemGroup>
</Project>

+ 20
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class AddBasketItemRequest
{
public int CatalogItemId { get; set; }
public string BasketId { get; set; }
public int Quantity { get; set; }
public AddBasketItemRequest()
{
Quantity = 1;
}
}
}

+ 31
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class BasketData
{
public string BuyerId { get; set; }
public List<BasketDataItem> Items { get; set; }
public BasketData(string buyerId)
{
BuyerId = buyerId;
Items = new List<BasketDataItem>();
}
}
public class BasketDataItem
{
public string Id { get; set; }
public string ProductId { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public decimal OldUnitPrice { get; set; }
public int Quantity { get; set; }
public string PictureUrl { get; set; }
}
}

+ 20
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class CatalogItem
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string PictureUri { get; set; }
}
}

+ 33
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs View File

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

+ 17
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class OrderItemData
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public decimal Discount { get; set; }
public int Units { get; set; }
public string PictureUrl { get; set; }
}
}

+ 31
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class UpdateBasketItemsRequest
{
public string BasketId { get; set; }
public ICollection<UpdateBasketItemData> Updates { get; set; }
public UpdateBasketItemsRequest()
{
Updates = new List<UpdateBasketItemData>();
}
}
public class UpdateBasketItemData
{
public string BasketItemId { get; set; }
public int NewQty { get; set; }
public UpdateBasketItemData()
{
NewQty = 0;
}
}
}

+ 21
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class UpdateBasketRequest
{
public string BuyerId { get; set; }
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
}
public class UpdateBasketRequestItemData
{
public string Id { get; set; } // Basket id
public int ProductId { get; set; } // Catalog item id
public int Quantity { get; set; } // Quantity
}
}

+ 36
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs View File

@ -0,0 +1,36 @@
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 Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost
.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(cb =>
{
var sources = cb.Sources;
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
{
Optional = true,
Path = "appsettings.localhost.json",
ReloadOnChange = false
});
})
.UseStartup<Startup>()
.Build();
}
}

+ 29
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Properties/launchSettings.json View File

@ -0,0 +1,29 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:57425/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"PurchaseForMvc": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:61632/"
}
}
}

+ 53
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs View File

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

+ 43
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs View File

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

+ 15
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs View File

@ -0,0 +1,15 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface IBasketService
{
Task<BasketData> GetById(string id);
Task Update(BasketData currentBasket);
}
}

+ 14
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs View File

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

+ 13
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs View File

@ -0,0 +1,13 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public interface IOrderApiClient
{
Task<OrderData> GetOrderDraftFromBasket(BasketData basket);
}
}

+ 37
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
{
public class OrderApiClient : IOrderApiClient
{
private readonly IHttpClient _apiClient;
private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls;
public OrderApiClient(IHttpClient httpClient, ILogger<OrderApiClient> logger, IOptionsSnapshot<UrlsConfig> config)
{
_apiClient = httpClient;
_logger = logger;
_urls = config.Value;
}
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket)
{
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var response = await _apiClient.PostAsync<BasketData>(url, basket);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<OrderData>(jsonResponse);
}
}
}

+ 138
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using Swashbuckle.AspNetCore.Swagger;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IHttpClient, StandardHttpClient>();
services.AddTransient<ICatalogService, CatalogService>();
services.AddTransient<IBasketService, BasketService>();
services.AddTransient<IOrderApiClient, OrderApiClient>();
services.AddOptions();
services.Configure<UrlsConfig>(Configuration.GetSection("urls"));
services.AddMvc();
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Shopping Aggregator for Mobile Clients",
Version = "v1",
Description = "Shopping Aggregator for Mobile Clients",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var identityUrl = Configuration.GetValue<string>("urls:identity");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "mobileshoppingagg";
options.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = async ctx =>
{
int i = 0;
},
OnTokenValidated = async ctx =>
{
int i = 0;
}
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
app.UsePathBase(pathBase);
}
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
});
}
}
}

+ 15
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.json View File

@ -0,0 +1,15 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}

+ 8
- 0
src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.localhost.json View File

@ -0,0 +1,8 @@
{
"urls": {
"basket": "http://localhost:55105",
"catalog": "http://localhost:55101",
"orders": "http://localhost:55102",
"identity": "http://localhost:55105"
}
}

+ 142
- 0
src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json View File

@ -0,0 +1,142 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/c/{everything}",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/b/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/o/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "mobileshoppingagg",
"Port": 80
}
],
"UpstreamPathTemplate": "/{everything}",
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/orders-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/basket-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/catalog-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/marketing-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "payment.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/payment-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/location-api/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 34
- 0
src/ApiGateways/Web.Bff.Marketing/apigw/configuration.json View File

@ -0,0 +1,34 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/m/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/l/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 31
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs View File

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

+ 133
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs View File

@ -0,0 +1,133 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class BasketController : Controller
{
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
public BasketController(ICatalogService catalogService, IBasketService basketService)
{
_catalog = catalogService;
_basket = basketService;
}
[HttpPost]
[HttpPut]
public async Task<IActionResult> UpdateAllBasket([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BuyerId);
if (currentBasket == null)
{
currentBasket = new BasketData(data.BuyerId);
}
var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId));
var newBasket = new BasketData(data.BuyerId);
foreach (var bitem in data.Items)
{
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
{
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
}
newBasket.Items.Add(new BasketDataItem()
{
Id = bitem.Id,
ProductId = catalogItem.Id.ToString(),
ProductName = catalogItem.Name,
PictureUrl = catalogItem.PictureUri,
UnitPrice = catalogItem.Price,
Quantity = bitem.Quantity
});
}
await _basket.Update(newBasket);
return Ok(newBasket);
}
[HttpPut]
[Route("items")]
public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
return BadRequest("No updates sent");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
}
// Update with new quantities
foreach (var update in data.Updates)
{
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
await _basket.Update(currentBasket);
return Ok(currentBasket);
}
[HttpPost]
[Route("items")]
public async Task<IActionResult> AddBasketItem([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
return BadRequest("Invalid payload");
}
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItem(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id.ToString(),
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
// Step 4: Update basket
await _basket.Update(currentBasket);
return Ok();
}
}
}

+ 18
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs View File

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

+ 42
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs View File

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers
{
[Route("api/v1/[controller]")]
[Authorize]
public class OrderController : Controller
{
private readonly IBasketService _basketService;
private readonly IOrderApiClient _orderClient;
public OrderController(IBasketService basketService, IOrderApiClient orderClient)
{
_basketService = basketService;
_orderClient = orderClient;
}
[Route("draft/{basketId}")]
[HttpGet]
public async Task<IActionResult> GetOrderDraft(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket);
return Ok(orderDraft);
}
}
}

+ 18
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile View File

@ -0,0 +1,18 @@
FROM microsoft/aspnetcore:2.0.5 AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/aspnetcore-build:2.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore -nowarn:msb3202,nu1503
WORKDIR /src/src/ApiGateways/Web.Bff.Shopping/aggregator
RUN dotnet build --no-restore -c Release -o /app
FROM build AS publish
RUN dotnet publish --no-restore -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Web.Shopping.HttpAggregator.dll"]

+ 33
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs View File

@ -0,0 +1,33 @@
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters
{
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
namespace Basket.API.Infrastructure.Filters
{
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 [] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" } }
});
}
}
}
}
}

+ 20
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class AddBasketItemRequest
{
public int CatalogItemId { get; set; }
public string BasketId { get; set; }
public int Quantity { get; set; }
public AddBasketItemRequest()
{
Quantity = 1;
}
}
}

+ 31
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class BasketData
{
public string BuyerId { get; set; }
public List<BasketDataItem> Items { get; set; }
public BasketData(string buyerId)
{
BuyerId = buyerId;
Items = new List<BasketDataItem>();
}
}
public class BasketDataItem
{
public string Id { get; set; }
public string ProductId { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public decimal OldUnitPrice { get; set; }
public int Quantity { get; set; }
public string PictureUrl { get; set; }
}
}

+ 20
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class CatalogItem
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string PictureUri { get; set; }
}
}

+ 33
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs View File

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

+ 17
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class OrderItemData
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public decimal Discount { get; set; }
public int Units { get; set; }
public string PictureUrl { get; set; }
}
}

+ 31
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class UpdateBasketItemsRequest
{
public string BasketId { get; set; }
public ICollection<UpdateBasketItemData> Updates { get; set; }
public UpdateBasketItemsRequest()
{
Updates = new List<UpdateBasketItemData>();
}
}
public class UpdateBasketItemData
{
public string BasketItemId { get; set; }
public int NewQty { get; set; }
public UpdateBasketItemData()
{
NewQty = 0;
}
}
}

+ 21
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models
{
public class UpdateBasketRequest
{
public string BuyerId { get; set; }
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
}
public class UpdateBasketRequestItemData
{
public string Id { get; set; } // Basket id
public int ProductId { get; set; } // Catalog item id
public int Quantity { get; set; } // Quantity
}
}

+ 36
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs View File

@ -0,0 +1,36 @@
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 Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost
.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(cb =>
{
var sources = cb.Sources;
sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource()
{
Optional = true,
Path = "appsettings.localhost.json",
ReloadOnChange = false
});
})
.UseStartup<Startup>()
.Build();
}
}

+ 29
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Properties/launchSettings.json View File

@ -0,0 +1,29 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:57425/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"PurchaseForMvc": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:61632/"
}
}
}

+ 53
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs View File

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

+ 43
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs View File

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

+ 15
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs View File

@ -0,0 +1,15 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface IBasketService
{
Task<BasketData> GetById(string id);
Task Update(BasketData currentBasket);
}
}

+ 14
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs View File

@ -0,0 +1,14 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface ICatalogService
{
Task<CatalogItem> GetCatalogItem(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids);
}
}

+ 13
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs View File

@ -0,0 +1,13 @@
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public interface IOrderApiClient
{
Task<OrderData> GetOrderDraftFromBasket(BasketData basket);
}
}

+ 37
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services
{
public class OrderApiClient : IOrderApiClient
{
private readonly IHttpClient _apiClient;
private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls;
public OrderApiClient(IHttpClient httpClient, ILogger<OrderApiClient> logger, IOptionsSnapshot<UrlsConfig> config)
{
_apiClient = httpClient;
_logger = logger;
_urls = config.Value;
}
public async Task<OrderData> GetOrderDraftFromBasket(BasketData basket)
{
var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var response = await _apiClient.PostAsync<BasketData>(url, basket);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<OrderData>(jsonResponse);
}
}
}

+ 138
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services;
using Swashbuckle.AspNetCore.Swagger;
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IHttpClient, StandardHttpClient>();
services.AddTransient<ICatalogService, CatalogService>();
services.AddTransient<IBasketService, BasketService>();
services.AddTransient<IOrderApiClient, OrderApiClient>();
services.AddOptions();
services.Configure<UrlsConfig>(Configuration.GetSection("urls"));
services.AddMvc();
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Shopping Aggregator for Web Clients",
Version = "v1",
Description = "Shopping Aggregator for Web Clients",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "webshoppingagg", "Shopping Aggregator for Web Clients" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var identityUrl = Configuration.GetValue<string>("urls:identity");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "webshoppingagg";
options.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = async ctx =>
{
int i = 0;
},
OnTokenValidated = async ctx =>
{
int i = 0;
}
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
app.UsePathBase(pathBase);
}
app.UseCors("CorsPolicy");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI");
});
}
}
}

+ 27
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Web.Shopping.HttpAggregator</AssemblyName>
<RootNamespace>Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator</RootNamespace>
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\Resilience\Resilience.Http\Resilience.Http.csproj" />
</ItemGroup>
</Project>

+ 15
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.json View File

@ -0,0 +1,15 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}

+ 8
- 0
src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.localhost.json View File

@ -0,0 +1,8 @@
{
"urls": {
"basket": "http://localhost:55105",
"catalog": "http://localhost:55101",
"orders": "http://localhost:55102",
"identity": "http://localhost:55105"
}
}

+ 142
- 0
src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json View File

@ -0,0 +1,142 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/c/{everything}",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/b/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/o/{everything}",
"UpstreamHttpMethod": [],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "webshoppingagg",
"Port": 80
}
],
"UpstreamPathTemplate": "/{everything}",
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "ordering.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/orders-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "basket.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/basket-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "catalog.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/catalog-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "marketing.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/marketing-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "payment.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/payment-api/{everything}",
"UpstreamHttpMethod": []
},
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "locations.api",
"Port": 80
}
],
"UpstreamPathTemplate": "/location-api/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}

+ 5
- 0
src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs View File

@ -97,6 +97,11 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
throw new HttpRequestException();
}
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadAsStringAsync();
});
}


+ 5
- 0
src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs View File

@ -36,6 +36,11 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
var response = await _client.SendAsync(requestMessage);
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadAsStringAsync();
}


+ 0
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/ObservableExtension.cs View File

@ -15,7 +15,6 @@ namespace eShopOnContainers.Core.Extensions
}
return collection;
}
}
}

+ 12
- 23
src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs View File

@ -4,7 +4,7 @@
{
public const string AzureTag = "Azure";
public const string MockTag = "Mock";
public const string DefaultEndpoint = "http://13.88.8.119";
public const string DefaultEndpoint = "http://YOUR_IP_OR_DNS_NAME"; // i.e.: "http://YOUR_IP" or "http://YOUR_DNS_NAME"
private string _baseEndpoint;
private static readonly GlobalSetting _instance = new GlobalSetting();
@ -38,18 +38,8 @@
public string RegisterWebsite { get; set; }
public string CatalogEndpoint { get; set; }
public string OrdersEndpoint { get; set; }
public string BasketEndpoint { get; set; }
public string IdentityEndpoint { get; set; }
public string LocationEndpoint { get; set; }
public string MarketingEndpoint { get; set; }
public string UserInfoEndpoint { get; set; }
public string TokenEndpoint { get; set; }
@ -62,18 +52,17 @@
private void UpdateEndpoint(string baseEndpoint)
{
RegisterWebsite = $"{baseEndpoint}:5105/Account/Register";
CatalogEndpoint = $"{baseEndpoint}:5101";
OrdersEndpoint = $"{baseEndpoint}:5102";
BasketEndpoint = $"{baseEndpoint}:5103";
IdentityEndpoint = $"{baseEndpoint}:5105/connect/authorize";
UserInfoEndpoint = $"{baseEndpoint}:5105/connect/userinfo";
TokenEndpoint = $"{baseEndpoint}:5105/connect/token";
LogoutEndpoint = $"{baseEndpoint}:5105/connect/endsession";
IdentityCallback = $"{baseEndpoint}:5105/xamarincallback";
LogoutCallback = $"{baseEndpoint}:5105/Account/Redirecting";
LocationEndpoint = $"{baseEndpoint}:5109";
MarketingEndpoint = $"{baseEndpoint}:5110";
var identityBaseEndpoint = $"{baseEndpoint}/identity";
RegisterWebsite = $"{identityBaseEndpoint}/Account/Register";
LogoutCallback = $"{identityBaseEndpoint}/Account/Redirecting";
var connectBaseEndpoint = $"{identityBaseEndpoint}/connect";
IdentityEndpoint = $"{connectBaseEndpoint}/authorize";
UserInfoEndpoint = $"{connectBaseEndpoint}/userinfo";
TokenEndpoint = $"{connectBaseEndpoint}/token";
LogoutEndpoint = $"{connectBaseEndpoint}/endsession";
IdentityCallback = $"{baseEndpoint}/xamarincallback";
}
}
}

+ 15
- 7
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs View File

@ -11,7 +11,7 @@ namespace eShopOnContainers.Core.Services.Basket
private readonly IRequestProvider _requestProvider;
private readonly IFixUriService _fixUriService;
private const string ApiUrlBase = "api/v1/basket";
private const string ApiUrlBase = "mobileshoppingapigw/api/v1/b/basket";
public BasketService(IRequestProvider requestProvider, IFixUriService fixUriService)
{
@ -21,15 +21,23 @@ namespace eShopOnContainers.Core.Services.Basket
public async Task<CustomerBasket> GetBasketAsync(string guidUser, string token)
{
var builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint)
var builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint)
{
Path = $"{ApiUrlBase}/{guidUser}"
};
var uri = builder.ToString();
CustomerBasket basket =
await _requestProvider.GetAsync<CustomerBasket>(uri, token);
CustomerBasket basket;
try
{
basket = await _requestProvider.GetAsync<CustomerBasket>(uri, token);
}
catch (HttpRequestExceptionEx exception) when (exception.HttpCode == System.Net.HttpStatusCode.NotFound)
{
basket = null;
}
_fixUriService.FixBasketItemPictureUri(basket?.Items);
return basket;
@ -37,7 +45,7 @@ namespace eShopOnContainers.Core.Services.Basket
public async Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token)
{
var builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint)
var builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint)
{
Path = ApiUrlBase
};
@ -49,7 +57,7 @@ namespace eShopOnContainers.Core.Services.Basket
public async Task CheckoutAsync(BasketCheckout basketCheckout, string token)
{
var builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint)
var builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint)
{
Path = $"{ApiUrlBase}/checkout"
};
@ -60,7 +68,7 @@ namespace eShopOnContainers.Core.Services.Basket
public async Task ClearBasketAsync(string guidUser, string token)
{
var builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint)
var builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint)
{
Path = $"{ApiUrlBase}/{guidUser}"
};


+ 10
- 8
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogService.cs View File

@ -13,6 +13,8 @@ namespace eShopOnContainers.Core.Services.Catalog
{
private readonly IRequestProvider _requestProvider;
private readonly IFixUriService _fixUriService;
private const string ApiUrlBase = "mobileshoppingapigw/api/v1/c/catalog";
public CatalogService(IRequestProvider requestProvider, IFixUriService fixUriService)
{
@ -22,8 +24,8 @@ namespace eShopOnContainers.Core.Services.Catalog
public async Task<ObservableCollection<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
builder.Path = string.Format("api/v1/catalog/items/type/{0}/brand/{1}", catalogTypeId, catalogBrandId);
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint);
builder.Path = $"{ApiUrlBase}/items/type/{catalogTypeId}/brand/{catalogBrandId}";
string uri = builder.ToString();
CatalogRoot catalog = await _requestProvider.GetAsync<CatalogRoot>(uri);
@ -36,8 +38,8 @@ namespace eShopOnContainers.Core.Services.Catalog
public async Task<ObservableCollection<CatalogItem>> GetCatalogAsync()
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
builder.Path = "api/v1/catalog/items";
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint);
builder.Path = $"{ApiUrlBase}/items";
string uri = builder.ToString();
CatalogRoot catalog = await _requestProvider.GetAsync<CatalogRoot>(uri);
@ -53,8 +55,8 @@ namespace eShopOnContainers.Core.Services.Catalog
public async Task<ObservableCollection<CatalogBrand>> GetCatalogBrandAsync()
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
builder.Path = "api/v1/catalog/catalogbrands";
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint);
builder.Path = $"{ApiUrlBase}/catalogbrands";
string uri = builder.ToString();
IEnumerable<CatalogBrand> brands = await _requestProvider.GetAsync<IEnumerable<CatalogBrand>>(uri);
@ -67,8 +69,8 @@ namespace eShopOnContainers.Core.Services.Catalog
public async Task<ObservableCollection<CatalogType>> GetCatalogTypeAsync()
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
builder.Path = "api/v1/catalog/catalogtypes";
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint);
builder.Path = $"{ApiUrlBase}/catalogtypes";
string uri = builder.ToString();
IEnumerable<CatalogType> types = await _requestProvider.GetAsync<IEnumerable<CatalogType>>(uri);


+ 2
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs View File

@ -15,8 +15,8 @@ namespace eShopOnContainers.Core.Services.Location
public async Task UpdateUserLocation(eShopOnContainers.Core.Models.Location.Location newLocReq, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.LocationEndpoint);
builder.Path = "api/v1/locations";
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint);
builder.Path = "/mobilemarketingapigw/api/v1/l/locations";
string uri = builder.ToString();
await _requestProvider.PostAsync(uri, newLocReq, token);
}


+ 6
- 4
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs View File

@ -13,6 +13,8 @@ namespace eShopOnContainers.Core.Services.Marketing
private readonly IRequestProvider _requestProvider;
private readonly IFixUriService _fixUriService;
private const string ApiUrlBase = "mobilemarketingapigw/api/v1/m/campaigns";
public CampaignService(IRequestProvider requestProvider, IFixUriService fixUriService)
{
_requestProvider = requestProvider;
@ -21,8 +23,8 @@ namespace eShopOnContainers.Core.Services.Marketing
public async Task<ObservableCollection<CampaignItem>> GetAllCampaignsAsync(string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.MarketingEndpoint);
builder.Path = "api/v1/campaigns/user";
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint);
builder.Path = $"{ApiUrlBase}/user";
string uri = builder.ToString();
CampaignRoot campaign = await _requestProvider.GetAsync<CampaignRoot>(uri, token);
@ -38,8 +40,8 @@ namespace eShopOnContainers.Core.Services.Marketing
public async Task<CampaignItem> GetCampaignByIdAsync(int campaignId, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.MarketingEndpoint);
builder.Path = $"api/v1/campaigns/{campaignId}";
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint);
builder.Path = $"{ApiUrlBase}/{campaignId}";
string uri = builder.ToString();
return await _requestProvider.GetAsync<CampaignItem>(uri, token);
}


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

Loading…
Cancel
Save