diff --git a/.dockerignore b/.dockerignore index be95b4e4c..676134dd4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -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/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index a8d5986bc..6e46dd41b 100644 --- a/.gitignore +++ b/.gitignore @@ -260,3 +260,4 @@ pub/ /src/Web/WebMVC/wwwroot/lib /src/Web/WebMVC/wwwroot/css/site.min.css **/.kube/** +.mfractor diff --git a/README.md b/README.md index b9b28ca81..3b3aa9077 100644 --- a/README.md +++ b/README.md @@ -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.

+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. ->

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. +>

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. >

> > Read the planned Roadmap and Milestones for future releases of eShopOnContainers within the Wiki for further info about possible new implementations and provide feedback at the ISSUES section 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 roadmap. +### 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 roadmap.

- +

> ### 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. +

-> 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.

> ### 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. >

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. >

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. diff --git a/docker-compose-external.override.yml b/docker-compose-external.override.yml index 5a4957115..4637385a1 100644 --- a/docker-compose-external.override.yml +++ b/docker-compose-external.override.yml @@ -1,4 +1,4 @@ -version: '3.3' +version: '3.4' services: sql.data: diff --git a/docker-compose-external.yml b/docker-compose-external.yml index 43a1559be..22fe4ce89 100644 --- a/docker-compose-external.yml +++ b/docker-compose-external.yml @@ -1,4 +1,4 @@ -version: '3.3' +version: '3.4' services: sql.data: diff --git a/docker-compose-windows.prod.yml b/docker-compose-windows.prod.yml index c9e9825e3..ead977f29 100644 --- a/docker-compose-windows.prod.yml +++ b/docker-compose-windows.prod.yml @@ -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: diff --git a/docker-compose-windows.yml b/docker-compose-windows.yml index 35f3ff7be..cec7323d1 100644 --- a/docker-compose-windows.yml +++ b/docker-compose-windows.yml @@ -1,4 +1,4 @@ -version: '3.3' +version: '3.4' services: basket.api: diff --git a/docker-compose.ci.build.yml b/docker-compose.ci.build.yml index e941c6ec1..747e8e583 100644 --- a/docker-compose.ci.build.yml +++ b/docker-compose.ci.build.yml @@ -1,4 +1,4 @@ -version: '3.3' +version: '3.4' services: ci-build: diff --git a/docker-compose.dcproj b/docker-compose.dcproj index 5f12f7d62..d1f485b05 100644 --- a/docker-compose.dcproj +++ b/docker-compose.dcproj @@ -7,6 +7,7 @@ webmvc Linux 2.1 + LaunchBrowser diff --git a/docker-compose.nobuild.yml b/docker-compose.nobuild.yml index bf1d9d24e..b2ebcf903 100644 --- a/docker-compose.nobuild.yml +++ b/docker-compose.nobuild.yml @@ -1,4 +1,4 @@ -version: '3.3' +version: '3.4' services: basket.api: diff --git a/docker-compose.override.windows.yml b/docker-compose.override.windows.yml index b4828607a..76ccda8b0 100644 --- a/docker-compose.override.windows.yml +++ b/docker-compose.override.windows.yml @@ -1,4 +1,4 @@ -version: '3.3' +version: '3.4' # ONLY NEEDED WHEN RUNNING WINDOWS CONTAINERS # diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 4f2e31123..093612225 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -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). \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index fac480f2e..2b6893ba0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -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" \ No newline at end of file + - "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). + diff --git a/docker-compose.yml b/docker-compose.yml index b237ad04f..122123644 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 \ No newline at end of file + 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 + diff --git a/docs/Azure Dev eBook_Booklet.pdf b/docs/Azure Dev eBook_Booklet.pdf new file mode 100644 index 000000000..ad55dd3d5 Binary files /dev/null and b/docs/Azure Dev eBook_Booklet.pdf differ diff --git a/docs/MicrosoftAzure_StartGuide_Developers.pdf b/docs/MicrosoftAzure_StartGuide_Developers.pdf new file mode 100644 index 000000000..b843ec51c Binary files /dev/null and b/docs/MicrosoftAzure_StartGuide_Developers.pdf differ diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index 353952ff0..d75a644ff 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.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} diff --git a/k8s/conf_cloud.yml b/k8s/conf_cloud.yml index 624d3a59b..a914105ae 100644 --- a/k8s/conf_cloud.yml +++ b/k8s/conf_cloud.yml @@ -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 \ No newline at end of file + 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 diff --git a/k8s/conf_local.yml b/k8s/conf_local.yml index 9a2059e63..615754bcf 100644 --- a/k8s/conf_local.yml +++ b/k8s/conf_local.yml @@ -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" diff --git a/k8s/deploy.ps1 b/k8s/deploy.ps1 index eed45ee62..27c5d3fde 100644 --- a/k8s/deploy.ps1 +++ b/k8s/deploy.ps1 @@ -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 diff --git a/k8s/deployments.yaml b/k8s/deployments.yaml index 034842f8c..a7f2f8d37 100644 --- a/k8s/deployments.yaml +++ b/k8s/deployments.yaml @@ -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 +--- diff --git a/k8s/frontend.yaml b/k8s/frontend.yaml deleted file mode 100644 index bd244beb2..000000000 --- a/k8s/frontend.yaml +++ /dev/null @@ -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 diff --git a/k8s/gen-k8s-env-aks.ps1 b/k8s/gen-k8s-env-aks.ps1 index a22588fa1..ef36a390d 100644 --- a/k8s/gen-k8s-env-aks.ps1 +++ b/k8s/gen-k8s-env-aks.ps1 @@ -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 -} \ No newline at end of file +} diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml index 3c2fe8bad..b5773bb25 100644 --- a/k8s/ingress.yaml +++ b/k8s/ingress.yaml @@ -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 - + diff --git a/k8s/internalurls.yaml b/k8s/internalurls.yaml new file mode 100644 index 000000000..e42ef23ec --- /dev/null +++ b/k8s/internalurls.yaml @@ -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 \ No newline at end of file diff --git a/k8s/ocelot/configuration-mobile-marketing.json b/k8s/ocelot/configuration-mobile-marketing.json new file mode 100644 index 000000000..666df1633 --- /dev/null +++ b/k8s/ocelot/configuration-mobile-marketing.json @@ -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" + } +} + \ No newline at end of file diff --git a/k8s/ocelot/configuration-mobile-shopping.json b/k8s/ocelot/configuration-mobile-shopping.json new file mode 100644 index 000000000..cf3a48aff --- /dev/null +++ b/k8s/ocelot/configuration-mobile-shopping.json @@ -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" + } + } + \ No newline at end of file diff --git a/k8s/ocelot/configuration-web-marketing.json b/k8s/ocelot/configuration-web-marketing.json new file mode 100644 index 000000000..666df1633 --- /dev/null +++ b/k8s/ocelot/configuration-web-marketing.json @@ -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" + } +} + \ No newline at end of file diff --git a/k8s/ocelot/configuration-web-shopping.json b/k8s/ocelot/configuration-web-shopping.json new file mode 100644 index 000000000..edf0714a0 --- /dev/null +++ b/k8s/ocelot/configuration-web-shopping.json @@ -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" + } + } + \ No newline at end of file diff --git a/k8s/ocelot/deployment.yaml b/k8s/ocelot/deployment.yaml new file mode 100644 index 000000000..3a114e4ce --- /dev/null +++ b/k8s/ocelot/deployment.yaml @@ -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 \ No newline at end of file diff --git a/k8s/ocelot/service.yaml b/k8s/ocelot/service.yaml new file mode 100644 index 000000000..858b54b21 --- /dev/null +++ b/k8s/ocelot/service.yaml @@ -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 diff --git a/k8s/services.yaml b/k8s/services.yaml index 805d3dc85..f7e5f2e45 100644 --- a/k8s/services.yaml +++ b/k8s/services.yaml @@ -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 diff --git a/src/ApiGateways/ApiGw-Base/Dockerfile b/src/ApiGateways/ApiGw-Base/Dockerfile new file mode 100644 index 000000000..7f0cf43a6 --- /dev/null +++ b/src/ApiGateways/ApiGw-Base/Dockerfile @@ -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"] diff --git a/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj new file mode 100644 index 000000000..d72d014b4 --- /dev/null +++ b/src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp2.0 + + + + + + + + + + + + diff --git a/src/ApiGateways/ApiGw-Base/Program.cs b/src/ApiGateways/ApiGw-Base/Program.cs new file mode 100644 index 000000000..782681901 --- /dev/null +++ b/src/ApiGateways/ApiGw-Base/Program.cs @@ -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(); + var host = builder.Build(); + return host; + } + } +} diff --git a/src/ApiGateways/ApiGw-Base/Properties/launchSettings.json b/src/ApiGateways/ApiGw-Base/Properties/launchSettings.json new file mode 100644 index 000000000..2308ca466 --- /dev/null +++ b/src/ApiGateways/ApiGw-Base/Properties/launchSettings.json @@ -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/" + } + } +} \ No newline at end of file diff --git a/src/ApiGateways/ApiGw-Base/Startup.cs b/src/ApiGateways/ApiGw-Base/Startup.cs new file mode 100644 index 000000000..f6a36b59e --- /dev/null +++ b/src/ApiGateways/ApiGw-Base/Startup.cs @@ -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("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(); + } + } +} diff --git a/src/ApiGateways/ApiGw-Base/appsettings.json b/src/ApiGateways/ApiGw-Base/appsettings.json new file mode 100644 index 000000000..426750e6a --- /dev/null +++ b/src/ApiGateways/ApiGw-Base/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": true, + "LogLevel": { + "Default": "Trace", + "System": "Information", + "Microsoft": "Information" + } + } +} \ No newline at end of file diff --git a/src/ApiGateways/Mobile.Bff.Marketing/apigw/configuration.json b/src/ApiGateways/Mobile.Bff.Marketing/apigw/configuration.json new file mode 100644 index 000000000..85d6777e6 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Marketing/apigw/configuration.json @@ -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" + } + } + \ No newline at end of file diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs new file mode 100644 index 000000000..e920fbbb2 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs @@ -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 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; } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs new file mode 100644 index 000000000..702c805fb --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs @@ -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 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 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 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(); + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs new file mode 100644 index 000000000..48a71480a --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs @@ -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"); + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs new file mode 100644 index 000000000..4c18d25ae --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs @@ -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 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); + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile new file mode 100644 index 000000000..273743cee --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile @@ -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"] + diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs new file mode 100644 index 000000000..b3a7246aa --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs @@ -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().Any() || + context.ApiDescription.ActionAttributes().OfType().Any(); + + if (hasAuthorize) + { + operation.Responses.Add("401", new Response { Description = "Unauthorized" }); + operation.Responses.Add("403", new Response { Description = "Forbidden" }); + + operation.Security = new List>>(); + operation.Security.Add(new Dictionary> + { + { "oauth2", new [] { "Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator" } } + }); + } + } + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj new file mode 100644 index 000000000..bd6d73950 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp2.0 + Mobile.Shopping.HttpAggregator + Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator + ..\..\..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + + + diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs new file mode 100644 index 000000000..f81842c09 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs @@ -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; + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs new file mode 100644 index 000000000..1b9348c44 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs @@ -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 Items { get; set; } + + public BasketData(string buyerId) + { + BuyerId = buyerId; + Items = new List(); + } + } + + 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; } + + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs new file mode 100644 index 000000000..25f766719 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs @@ -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; } + + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs new file mode 100644 index 000000000..e87cc18f0 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs @@ -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 OrderItems { get; } = new List(); + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs new file mode 100644 index 000000000..b0179e470 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs @@ -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; } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs new file mode 100644 index 000000000..43cc81b89 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs @@ -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 Updates { get; set; } + + public UpdateBasketItemsRequest() + { + Updates = new List(); + } + } + + public class UpdateBasketItemData + { + public string BasketItemId { get; set; } + public int NewQty { get; set; } + + public UpdateBasketItemData() + { + NewQty = 0; + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs new file mode 100644 index 000000000..cb22bf55f --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs @@ -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 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 + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs new file mode 100644 index 000000000..0c88fcd7d --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs @@ -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() + .Build(); + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Properties/launchSettings.json b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Properties/launchSettings.json new file mode 100644 index 000000000..925e70b0d --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Properties/launchSettings.json @@ -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/" + } + } +} \ No newline at end of file diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs new file mode 100644 index 000000000..b00fbb52c --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs @@ -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 _logger; + private readonly UrlsConfig _urls; + private readonly IHttpContextAccessor _httpContextAccessor; + + public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + _httpContextAccessor = httpContextAccessor; + } + + public async Task 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(data) : null; + return basket; + } + + public async Task Update(BasketData currentBasket) + { + var token = await GetUserTokenAsync(); + var data = await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token); + int i = 0; + } + + async Task GetUserTokenAsync() + { + var context = _httpContextAccessor.HttpContext; + return await context.GetTokenAsync("access_token"); + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs new file mode 100644 index 000000000..d37b67679 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs @@ -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 _logger; + private readonly UrlsConfig _urls; + + public CatalogService(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } + + public async Task GetCatalogItem(int id) + { + var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); + var item = JsonConvert.DeserializeObject(data); + return item; + } + + public async Task> GetCatalogItems(IEnumerable ids) + { + var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); + var item = JsonConvert.DeserializeObject(data); + return item; + + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs new file mode 100644 index 000000000..6fd6871c1 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs @@ -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 GetById(string id); + Task Update(BasketData currentBasket); + + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs new file mode 100644 index 000000000..d7e605bfa --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs @@ -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 GetCatalogItem(int id); + Task> GetCatalogItems(IEnumerable ids); + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs new file mode 100644 index 000000000..30ac013b9 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs @@ -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 GetOrderDraftFromBasket(BasketData basket); + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs new file mode 100644 index 000000000..2659e11cc --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -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 _logger; + private readonly UrlsConfig _urls; + + public OrderApiClient(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } + + public async Task GetOrderDraftFromBasket(BasketData basket) + { + var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); + var response = await _apiClient.PostAsync(url, basket); + response.EnsureSuccessStatusCode(); + var jsonResponse = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(jsonResponse); + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs new file mode 100644 index 000000000..73b736519 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs @@ -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(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddOptions(); + services.Configure(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("IdentityUrlExternal")}/connect/authorize", + TokenUrl = $"{Configuration.GetValue("IdentityUrlExternal")}/connect/token", + Scopes = new Dictionary() + { + { "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } + } + }); + + options.OperationFilter(); + }); + + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + var identityUrl = Configuration.GetValue("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"); + }); + + + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.json b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.json new file mode 100644 index 000000000..26bb0ac7a --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning" + } + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.localhost.json b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.localhost.json new file mode 100644 index 000000000..57b5e894d --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.localhost.json @@ -0,0 +1,8 @@ +{ + "urls": { + "basket": "http://localhost:55105", + "catalog": "http://localhost:55101", + "orders": "http://localhost:55102", + "identity": "http://localhost:55105" + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json b/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json new file mode 100644 index 000000000..870690ed4 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json @@ -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" + } + } + \ No newline at end of file diff --git a/src/ApiGateways/Web.Bff.Marketing/apigw/configuration.json b/src/ApiGateways/Web.Bff.Marketing/apigw/configuration.json new file mode 100644 index 000000000..8afe4a4d6 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Marketing/apigw/configuration.json @@ -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" + } +} + \ No newline at end of file diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs new file mode 100644 index 000000000..19be27dce --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs @@ -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 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; } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs new file mode 100644 index 000000000..bfef55726 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs @@ -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 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 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 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(); + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs new file mode 100644 index 000000000..58ed48d1a --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs @@ -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"); + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs new file mode 100644 index 000000000..fd108ffb8 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs @@ -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 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); + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile new file mode 100644 index 000000000..ce6f1b155 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile @@ -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"] diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs new file mode 100644 index 000000000..3f382e5df --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs @@ -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().Any() || + context.ApiDescription.ActionAttributes().OfType().Any(); + + if (hasAuthorize) + { + operation.Responses.Add("401", new Response { Description = "Unauthorized" }); + operation.Responses.Add("403", new Response { Description = "Forbidden" }); + + operation.Security = new List>>(); + operation.Security.Add(new Dictionary> + { + { "oauth2", new [] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" } } + }); + } + } + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs new file mode 100644 index 000000000..88aff245f --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs @@ -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; + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs new file mode 100644 index 000000000..01831a5c9 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs @@ -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 Items { get; set; } + + public BasketData(string buyerId) + { + BuyerId = buyerId; + Items = new List(); + } + } + + 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; } + + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs new file mode 100644 index 000000000..c6085f934 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs @@ -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; } + + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs new file mode 100644 index 000000000..e9d5982b9 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs @@ -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 OrderItems { get; } = new List(); + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs new file mode 100644 index 000000000..1a40cb8cb --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs @@ -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; } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs new file mode 100644 index 000000000..b41c069bc --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs @@ -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 Updates { get; set; } + + public UpdateBasketItemsRequest() + { + Updates = new List(); + } + } + + public class UpdateBasketItemData + { + public string BasketItemId { get; set; } + public int NewQty { get; set; } + + public UpdateBasketItemData() + { + NewQty = 0; + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs new file mode 100644 index 000000000..9beeeade4 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs @@ -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 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 + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs new file mode 100644 index 000000000..c865a8b3b --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs @@ -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() + .Build(); + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Properties/launchSettings.json b/src/ApiGateways/Web.Bff.Shopping/aggregator/Properties/launchSettings.json new file mode 100644 index 000000000..925e70b0d --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Properties/launchSettings.json @@ -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/" + } + } +} \ No newline at end of file diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs new file mode 100644 index 000000000..5ca89a408 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs @@ -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 _logger; + private readonly UrlsConfig _urls; + private readonly IHttpContextAccessor _httpContextAccessor; + + public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + _httpContextAccessor = httpContextAccessor; + } + + public async Task 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(data) : null; + return basket; + } + + public async Task Update(BasketData currentBasket) + { + var token = await GetUserTokenAsync(); + var data = await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token); + int i = 0; + } + + async Task GetUserTokenAsync() + { + var context = _httpContextAccessor.HttpContext; + return await context.GetTokenAsync("access_token"); + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs new file mode 100644 index 000000000..46d895f68 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs @@ -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 _logger; + private readonly UrlsConfig _urls; + + public CatalogService(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } + + public async Task GetCatalogItem(int id) + { + var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); + var item = JsonConvert.DeserializeObject(data); + return item; + } + + public async Task> GetCatalogItems(IEnumerable ids) + { + var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); + var item = JsonConvert.DeserializeObject(data); + return item; + + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs new file mode 100644 index 000000000..f59c31965 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs @@ -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 GetById(string id); + Task Update(BasketData currentBasket); + + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs new file mode 100644 index 000000000..7d192f3cc --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs @@ -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 GetCatalogItem(int id); + Task> GetCatalogItems(IEnumerable ids); + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs new file mode 100644 index 000000000..c97eccbbd --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs @@ -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 GetOrderDraftFromBasket(BasketData basket); + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs new file mode 100644 index 000000000..220e9afa9 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -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 _logger; + private readonly UrlsConfig _urls; + + public OrderApiClient(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } + + public async Task GetOrderDraftFromBasket(BasketData basket) + { + var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); + var response = await _apiClient.PostAsync(url, basket); + response.EnsureSuccessStatusCode(); + var jsonResponse = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(jsonResponse); + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs new file mode 100644 index 000000000..17f9f90e6 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs @@ -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(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddOptions(); + services.Configure(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("IdentityUrlExternal")}/connect/authorize", + TokenUrl = $"{Configuration.GetValue("IdentityUrlExternal")}/connect/token", + Scopes = new Dictionary() + { + { "webshoppingagg", "Shopping Aggregator for Web Clients" } + } + }); + + options.OperationFilter(); + }); + + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + var identityUrl = Configuration.GetValue("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"); + }); + + + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj new file mode 100644 index 000000000..0b6dbf44b --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp2.0 + Web.Shopping.HttpAggregator + Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator + ..\..\..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + + + diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.json b/src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.json new file mode 100644 index 000000000..26bb0ac7a --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning" + } + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.localhost.json b/src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.localhost.json new file mode 100644 index 000000000..57b5e894d --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.localhost.json @@ -0,0 +1,8 @@ +{ + "urls": { + "basket": "http://localhost:55105", + "catalog": "http://localhost:55101", + "orders": "http://localhost:55102", + "identity": "http://localhost:55105" + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json b/src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json new file mode 100644 index 000000000..63aeb7752 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/apigw/configuration.json @@ -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" + } + } + \ No newline at end of file diff --git a/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs b/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs index 18051a501..9fa38ee8d 100644 --- a/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs +++ b/src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs @@ -97,6 +97,11 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http throw new HttpRequestException(); } + if (!response.IsSuccessStatusCode) + { + return null; + } + return await response.Content.ReadAsStringAsync(); }); } diff --git a/src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs b/src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs index a5f6a63c4..578168bff 100644 --- a/src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs +++ b/src/BuildingBlocks/Resilience/Resilience.Http/StandardHttpClient.cs @@ -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(); } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/ObservableExtension.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/ObservableExtension.cs index ad10bd941..800f9a031 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/ObservableExtension.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/ObservableExtension.cs @@ -15,7 +15,6 @@ namespace eShopOnContainers.Core.Extensions } return collection; - } } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs index d501a5d46..e1637d7be 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs @@ -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"; } } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs index 54f3a03bc..b36488f24 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketService.cs @@ -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 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(uri, token); + CustomerBasket basket; + + try + { + basket = await _requestProvider.GetAsync(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 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}" }; diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogService.cs index 2e63e9516..2811416ad 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogService.cs @@ -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> 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(uri); @@ -36,8 +38,8 @@ namespace eShopOnContainers.Core.Services.Catalog public async Task> 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(uri); @@ -53,8 +55,8 @@ namespace eShopOnContainers.Core.Services.Catalog public async Task> 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 brands = await _requestProvider.GetAsync>(uri); @@ -67,8 +69,8 @@ namespace eShopOnContainers.Core.Services.Catalog public async Task> 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 types = await _requestProvider.GetAsync>(uri); diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs index f20ae6339..e18d335c0 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs @@ -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); } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs index cb74f11b0..2fa5eaa9e 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignService.cs @@ -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> 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(uri, token); @@ -38,8 +40,8 @@ namespace eShopOnContainers.Core.Services.Marketing public async Task 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(uri, token); } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs index f242971fb..fb9a0c627 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderService.cs @@ -11,6 +11,8 @@ namespace eShopOnContainers.Core.Services.Order { private readonly IRequestProvider _requestProvider; + private const string ApiUrlBase = "mobileshoppingapigw/api/v1/o/orders"; + public OrderService(IRequestProvider requestProvider) { _requestProvider = requestProvider; @@ -23,9 +25,9 @@ namespace eShopOnContainers.Core.Services.Order public async Task> GetOrdersAsync(string token) { - UriBuilder builder = new UriBuilder(GlobalSetting.Instance.OrdersEndpoint); + UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint); - builder.Path = "api/v1/orders"; + builder.Path = ApiUrlBase; string uri = builder.ToString(); @@ -40,9 +42,9 @@ namespace eShopOnContainers.Core.Services.Order { try { - UriBuilder builder = new UriBuilder(GlobalSetting.Instance.OrdersEndpoint); + UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint); - builder.Path = string.Format("api/v1/orders/{0}", orderId); + builder.Path = $"{ApiUrlBase}/{orderId}"; string uri = builder.ToString(); @@ -76,9 +78,9 @@ namespace eShopOnContainers.Core.Services.Order public async Task CancelOrderAsync(int orderId, string token) { - UriBuilder builder = new UriBuilder(GlobalSetting.Instance.OrdersEndpoint); + UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BaseEndpoint); - builder.Path = "api/v1/orders/cancel"; + builder.Path = $"{ApiUrlBase}/cancel"; var cancelOrderCommand = new CancelOrderCommand(orderId); diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/ISettingsService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/ISettingsService.cs index 5e2732bf9..69ede7b72 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/ISettingsService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/ISettingsService.cs @@ -1,4 +1,6 @@ -namespace eShopOnContainers.Core.Services.Settings +using System.Threading.Tasks; + +namespace eShopOnContainers.Core.Services.Settings { public interface ISettingsService { @@ -10,5 +12,10 @@ string Latitude { get; set; } string Longitude { get; set; } bool AllowGpsLocation { get; set; } + + bool GetValueOrDefault(string key, bool defaultValue); + string GetValueOrDefault(string key, string defaultValue); + Task AddOrUpdateValue(string key, bool value); + Task AddOrUpdateValue(string key, string value); } } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/ISettingsServiceImplementation.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/ISettingsServiceImplementation.cs deleted file mode 100644 index ef9d14ed6..000000000 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/ISettingsServiceImplementation.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace eShopOnContainers.Core.Services.Settings -{ - public interface ISettingsServiceImplementation - { - bool GetValueOrDefault(string key, bool defaultValue); - string GetValueOrDefault(string key, string defaultValue); - - bool AddOrUpdateValue(string key, bool value); - bool AddOrUpdateValue(string key, string value); - - void Remove(string key); - } -} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/SettingsService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/SettingsService.cs index a13f53187..ca3e03c04 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/SettingsService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Settings/SettingsService.cs @@ -1,21 +1,11 @@ -using eShopOnContainers.Core.Services.Dependency; +using System; +using System.Threading.Tasks; +using Xamarin.Forms; namespace eShopOnContainers.Core.Services.Settings { public class SettingsService : ISettingsService { - private readonly ISettingsServiceImplementation _settingsService; - - ISettingsServiceImplementation AppSettings - { - get { return _settingsService; } - } - - public SettingsService(IDependencyService dependencyService) - { - _settingsService = dependencyService.Get(); - } - #region Setting Constants private const string AccessToken = "access_token"; @@ -37,52 +27,113 @@ namespace eShopOnContainers.Core.Services.Settings #endregion + #region Settings Properties + public string AuthAccessToken { - get => AppSettings.GetValueOrDefault(AccessToken, AccessTokenDefault); - set => AppSettings.AddOrUpdateValue(AccessToken, value); + get => GetValueOrDefault(AccessToken, AccessTokenDefault); + set => AddOrUpdateValue(AccessToken, value); } public string AuthIdToken { - get => AppSettings.GetValueOrDefault(IdToken, IdTokenDefault); - set => AppSettings.AddOrUpdateValue(IdToken, value); + get => GetValueOrDefault(IdToken, IdTokenDefault); + set => AddOrUpdateValue(IdToken, value); } public bool UseMocks { - get => AppSettings.GetValueOrDefault(IdUseMocks, UseMocksDefault); - set => AppSettings.AddOrUpdateValue(IdUseMocks, value); + get => GetValueOrDefault(IdUseMocks, UseMocksDefault); + set => AddOrUpdateValue(IdUseMocks, value); } public string UrlBase { - get => AppSettings.GetValueOrDefault(IdUrlBase, UrlBaseDefault); - set => AppSettings.AddOrUpdateValue(IdUrlBase, value); + get => GetValueOrDefault(IdUrlBase, UrlBaseDefault); + set => AddOrUpdateValue(IdUrlBase, value); } public bool UseFakeLocation { - get => AppSettings.GetValueOrDefault(IdUseFakeLocation, UseFakeLocationDefault); - set => AppSettings.AddOrUpdateValue(IdUseFakeLocation, value); + get => GetValueOrDefault(IdUseFakeLocation, UseFakeLocationDefault); + set => AddOrUpdateValue(IdUseFakeLocation, value); } public string Latitude { - get => AppSettings.GetValueOrDefault(IdLatitude, FakeLatitudeDefault.ToString()); - set => AppSettings.AddOrUpdateValue(IdLatitude, value); + get => GetValueOrDefault(IdLatitude, FakeLatitudeDefault.ToString()); + set => AddOrUpdateValue(IdLatitude, value); } public string Longitude { - get => AppSettings.GetValueOrDefault(IdLongitude, FakeLongitudeDefault.ToString()); - set => AppSettings.AddOrUpdateValue(IdLongitude, value); + get => GetValueOrDefault(IdLongitude, FakeLongitudeDefault.ToString()); + set => AddOrUpdateValue(IdLongitude, value); } public bool AllowGpsLocation { - get => AppSettings.GetValueOrDefault(IdAllowGpsLocation, AllowGpsLocationDefault); - set => AppSettings.AddOrUpdateValue(IdAllowGpsLocation, value); + get => GetValueOrDefault(IdAllowGpsLocation, AllowGpsLocationDefault); + set => AddOrUpdateValue(IdAllowGpsLocation, value); } + + #endregion + + #region Public Methods + + public Task AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); + public Task AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); + public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); + public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); + + #endregion + + #region Internal Implementation + + async Task AddOrUpdateValueInternal(string key, T value) + { + if (value == null) + { + await Remove(key); + } + + Application.Current.Properties[key] = value; + try + { + await Application.Current.SavePropertiesAsync(); + } + catch (Exception ex) + { + Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message); + } + } + + T GetValueOrDefaultInternal(string key, T defaultValue = default(T)) + { + object value = null; + if (Application.Current.Properties.ContainsKey(key)) + { + value = Application.Current.Properties[key]; + } + return null != value ? (T)value : defaultValue; + } + + async Task Remove(string key) + { + try + { + if (Application.Current.Properties[key] != null) + { + Application.Current.Properties.Remove(key); + await Application.Current.SavePropertiesAsync(); + } + } + catch (Exception ex) + { + Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message); + } + } + + #endregion } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml index f6b9bd72c..2bccd06d6 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml @@ -337,28 +337,14 @@ Grid.RowSpan="2" IsVisible="{Binding IsLogin}"> + Source="{Binding LoginUrl}" + AbsoluteLayout.LayoutBounds="0, 0, 1, 1" + AbsoluteLayout.LayoutFlags="All"> - - - - - - - - - - - - + diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Extensions/LocationExtensions.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Extensions/LocationExtensions.cs index 41ea2ad3e..031c186aa 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Extensions/LocationExtensions.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Extensions/LocationExtensions.cs @@ -32,7 +32,7 @@ namespace eShopOnContainers.Droid.Extensions { return new DateTimeOffset(Epoch.AddMilliseconds(location.Time)); } - catch (Exception e) + catch (Exception) { return new DateTimeOffset(Epoch); } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Services/SettingsServiceImplementation.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Services/SettingsServiceImplementation.cs deleted file mode 100644 index 7cdce4555..000000000 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Services/SettingsServiceImplementation.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Android.App; -using Android.Content; -using Android.Preferences; -using eShopOnContainers.Core.Services.Settings; -using eShopOnContainers.Droid.Services; -using System; - -[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))] -namespace eShopOnContainers.Droid.Services -{ - public class SettingsServiceImplementation : ISettingsServiceImplementation - { - #region Internal Implementation - - readonly object _locker = new object(); - - ISharedPreferences GetSharedPreference() - { - return PreferenceManager.GetDefaultSharedPreferences(Application.Context); - } - - bool AddOrUpdateValueInternal(string key, T value) - { - if (Application.Context == null) - return false; - - if (value == null) - { - Remove(key); - return true; - } - - var type = typeof(T); - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - type = Nullable.GetUnderlyingType(type); - } - var typeCode = Type.GetTypeCode(type); - - lock (_locker) - { - using (var sharedPrefs = GetSharedPreference()) - { - using (var editor = sharedPrefs.Edit()) - { - switch (typeCode) - { - case TypeCode.Boolean: - editor.PutBoolean(key, Convert.ToBoolean(value)); - break; - case TypeCode.String: - editor.PutString(key, Convert.ToString(value)); - break; - default: - throw new ArgumentException($"Value of type {typeCode} is not supported."); - } - editor.Commit(); - } - } - } - return true; - } - - T GetValueOrDefaultInternal(string key, T defaultValue = default(T)) - { - if (Application.Context == null) - return defaultValue; - - if (!Contains(key)) - return defaultValue; - - lock (_locker) - { - using (var sharedPrefs = GetSharedPreference()) - { - var type = typeof(T); - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - type = Nullable.GetUnderlyingType(type); - } - - object value = null; - var typeCode = Type.GetTypeCode(type); - switch (typeCode) - { - case TypeCode.Boolean: - value = sharedPrefs.GetBoolean(key, Convert.ToBoolean(defaultValue)); - break; - case TypeCode.String: - value = sharedPrefs.GetString(key, Convert.ToString(defaultValue)); - break; - default: - throw new ArgumentException($"Value of type {typeCode} is not supported."); - } - - return null != value ? (T)value : defaultValue; - } - } - } - - bool Contains(string key) - { - if (Application.Context == null) - return false; - - lock (_locker) - { - using (var sharedPrefs = GetSharedPreference()) - { - if (sharedPrefs == null) - return false; - return sharedPrefs.Contains(key); - } - } - } - - #endregion - - #region ISettingsServiceImplementation - - public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); - - public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); - - public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); - - public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); - - public void Remove(string key) - { - if (Application.Context == null) - return; - - lock (_locker) - { - using (var sharedPrefs = GetSharedPreference()) - { - using (var editor = sharedPrefs.Edit()) - { - editor.Remove(key); - editor.Commit(); - } - } - } - } - - #endregion - } -} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj index 7d699f9d2..92e2df581 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj @@ -19,7 +19,7 @@ Off Properties\AndroidManifest.xml true - v8.0 + v8.1 armeabi,armeabi-v7a,x86 @@ -211,7 +211,6 @@ - diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj.bak b/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj.bak deleted file mode 100644 index 8a2babd2e..000000000 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Droid/eShopOnContainers.Droid.csproj.bak +++ /dev/null @@ -1,429 +0,0 @@ - - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {62DBB163-9CA9-4818-B48B-13233DF37C24} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - eShopOnContainers.Droid - eShopOnContainers.Droid - 512 - true - Resources\Resource.Designer.cs - Off - Properties\AndroidManifest.xml - true - v8.0 - armeabi,armeabi-v7a,x86 - - - 1G - - - - - - true - - - True - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - 1G - Xamarin - armeabi;armeabi-v7a;x86 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - False - SdkOnly - - - - ..\..\..\..\packages\Acr.Support.2.1.0\lib\MonoAndroid10\Acr.Support.Android.dll - True - - - ..\..\..\..\packages\AndHUD.1.2.0\lib\MonoAndroid\AndHUD.dll - True - - - ..\..\..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\MonoAndroid10\FFImageLoading.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.2.2.9\lib\MonoAndroid10\FFImageLoading.Platform.dll - - - ..\..\..\..\packages\IdentityModel.1.3.1\lib\portable-net45+wp80+win8+wpa81\IdentityModel.Portable.dll - True - - - - ..\..\..\..\packages\modernhttpclient.2.4.2\lib\MonoAndroid\ModernHttpClient.dll - True - - - - - ..\..\..\..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll - - - ..\..\..\..\packages\modernhttpclient.2.4.2\lib\MonoAndroid\OkHttp.dll - True - - - ..\..\..\..\packages\PCLCrypto.2.0.147\lib\MonoAndroid23\PCLCrypto.dll - - - ..\..\..\..\packages\PInvoke.BCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.BCrypt.dll - - - ..\..\..\..\packages\PInvoke.Kernel32.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Kernel32.dll - - - ..\..\..\..\packages\PInvoke.NCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.NCrypt.dll - - - ..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll - - - ..\..\..\..\packages\Plugin.CurrentActivity.1.0.1\lib\MonoAndroid10\Plugin.CurrentActivity.dll - - - ..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\MonoAndroid10\Plugin.Geolocator.dll - - - ..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\MonoAndroid10\Plugin.Geolocator.Abstractions.dll - - - ..\..\..\..\packages\Plugin.Permissions.1.1.7\lib\MonoAndroid10\Plugin.Permissions.dll - - - ..\..\..\..\packages\Plugin.Permissions.1.1.7\lib\MonoAndroid10\Plugin.Permissions.Abstractions.dll - - - ..\..\..\..\packages\Xam.Plugins.Settings.2.6.0.12-beta\lib\MonoAndroid10\Plugin.Settings.dll - True - - - ..\..\..\..\packages\Xam.Plugins.Settings.2.6.0.12-beta\lib\MonoAndroid10\Plugin.Settings.Abstractions.dll - True - - - ..\..\..\..\packages\SlideOverKit.2.1.4\lib\MonoAndroid10\SlideOverKit.dll - True - - - ..\..\..\..\packages\SlideOverKit.2.1.4\lib\MonoAndroid10\SlideOverKit.Droid.dll - True - - - ..\..\..\..\packages\Splat.1.6.2\lib\monoandroid\Splat.dll - True - - - - - - ..\..\..\..\packages\Microsoft.Net.Http.2.2.29\lib\monoandroid\System.Net.Http.Extensions.dll - True - - - ..\..\..\..\packages\Microsoft.Net.Http.2.2.29\lib\monoandroid\System.Net.Http.Primitives.dll - True - - - - - ..\..\..\..\packages\Validation.2.2.8\lib\dotnet\Validation.dll - - - - ..\..\..\..\packages\Autofac.4.5.0\lib\netstandard1.1\Autofac.dll - - - ..\..\..\..\packages\Acr.UserDialogs.6.3.8\lib\MonoAndroid10\Acr.UserDialogs.dll - - - ..\..\..\..\packages\Acr.UserDialogs.6.3.8\lib\MonoAndroid10\Acr.UserDialogs.Interface.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.91635\lib\MonoAndroid10\FormsViewGroup.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.91635\lib\MonoAndroid10\Xamarin.Forms.Core.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.91635\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.91635\lib\MonoAndroid10\Xamarin.Forms.Platform.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.91635\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.2.9\lib\MonoAndroid10\FFImageLoading.Forms.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.2.9\lib\MonoAndroid10\FFImageLoading.Forms.Droid.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Vector.Drawable.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.v7.RecyclerView.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Annotations.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Annotations.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Compat.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Compat.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.v7.CardView.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.CardView.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Media.Compat.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Media.Compat.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Core.UI.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Core.UI.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Core.Utils.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Core.Utils.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Fragment.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Fragment.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Design.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Design.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.Transition.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.Transition.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.v4.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v4.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.v7.Palette.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.Palette.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.v7.AppCompat.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.dll - - - ..\..\..\..\packages\Xamarin.Android.Support.v7.MediaRouter.25.4.0.2\lib\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.dll - - - - - - - - - - - - - - - - - - - - - Assets\Montserrat-Bold.ttf - - - Assets\Montserrat-Regular.ttf - - - Assets\SourceSansPro-Regular.ttf - - - - - - - - - Designer - - - Designer - - - Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {ba96a12c-4ee3-46c4-bb3f-f811b554cd01} - eShopOnContainers.Core - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj index 4a83a29a8..250d836a7 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj @@ -1,232 +1,232 @@  - - - - Debug - iPhoneSimulator - {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3} - {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Exe - eShopOnContainers.TestRunner.iOS - Resources - eShopOnContainersTestRunneriOS - - - - - true - full - false - bin\iPhoneSimulator\Debug - DEBUG - prompt - 4 - false - x86_64 - None - True - False - False - False - False - False - True - Default - HttpClientHandler - False - - - false - none - true - bin\iPhoneSimulator\Release - prompt - 4 - None - x86_64 - false - - - true - full - false - bin\iPhone\Debug - DEBUG - prompt - 4 - false - ARMv7, ARM64 - Entitlements.plist - iPhone Developer - true - None - - - false - none - true - bin\iPhone\Release - prompt - 4 - Entitlements.plist - ARMv7, ARM64 - false - iPhone Developer - - - none - True - bin\iPhone\Ad-Hoc - prompt - 4 - False - ARMv7, ARM64 - Entitlements.plist - True - Automatic:AdHoc - iPhone Distribution - - - none - True - bin\iPhone\AppStore - prompt - 4 - False - ARMv7, ARM64 - Entitlements.plist - Automatic:AppStore - iPhone Distribution - - - - - - - - - - - - - - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll - - - - - - ..\..\..\..\packages\xunit.runner.devices.2.3.3\lib\xamarinios10\xunit.runner.devices.dll - - - ..\..\..\..\packages\xunit.runner.devices.2.3.3\lib\xamarinios10\xunit.runner.utility.netstandard15.dll - - - ..\..\..\..\packages\xunit.abstractions.2.0.1\lib\netstandard1.0\xunit.abstractions.dll - - - ..\..\..\..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll - - - ..\..\..\..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll - - - ..\..\..\..\packages\xunit.extensibility.execution.2.3.1\lib\netstandard1.1\xunit.execution.dotnet.dll - - - - ..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll - - - ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.dll - - - ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.iOS.dll - - - ..\..\..\..\packages\IdentityModel.3.0.0\lib\netstandard2.0\IdentityModel.dll - - - ..\..\..\..\packages\Acr.Support.2.1.0\lib\Xamarin.iOS10\Acr.Support.iOS.dll - - - ..\..\..\..\packages\BTProgressHUD.1.2.0.5\lib\Xamarin.iOS10\BTProgressHUD.dll - - - ..\..\..\..\packages\Splat.2.0.0\lib\Xamarin.iOS10\Splat.dll - - - ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.dll - - - ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.Interface.dll - - - ..\..\..\..\packages\WebP.Touch.1.0.7\lib\Xamarin.iOS10\WebP.Touch.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Platform.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.Touch.dll - - - ..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll - - - ..\..\..\..\packages\PInvoke.Kernel32.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Kernel32.dll - - - ..\..\..\..\packages\PInvoke.BCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.BCrypt.dll - - - ..\..\..\..\packages\PInvoke.NCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.NCrypt.dll - - - ..\..\..\..\packages\Validation.2.2.8\lib\dotnet\Validation.dll - - - ..\..\..\..\packages\PCLCrypto.2.0.147\lib\xamarinios10\PCLCrypto.dll - - - - - - - - {FDD910BC-DF0F-483D-B7D5-C7D831855172} - eShopOnContainers.UnitTests - - - - - - + + - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + Debug + iPhoneSimulator + {B68C2B56-7581-46AE-B55D-D25DDFD3BFE3} + {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Exe + eShopOnContainers.TestRunner.iOS + Resources + eShopOnContainersTestRunneriOS + + + true - - - - - - + + true + full + false + bin\iPhoneSimulator\Debug + DEBUG + prompt + 4 + false + x86_64 + None + True + False + False + False + False + False + True + Default + HttpClientHandler + False + + + false + none + true + bin\iPhoneSimulator\Release + prompt + 4 + None + x86_64 + false + + + true + full + false + bin\iPhone\Debug + DEBUG + prompt + 4 + false + ARMv7, ARM64 + Entitlements.plist + iPhone Developer + true + None + + + false + none + true + bin\iPhone\Release + prompt + 4 + Entitlements.plist + ARMv7, ARM64 + false + iPhone Developer + + + none + True + bin\iPhone\Ad-Hoc + prompt + 4 + False + ARMv7, ARM64 + Entitlements.plist + True + Automatic:AdHoc + iPhone Distribution + + + none + True + bin\iPhone\AppStore + prompt + 4 + False + ARMv7, ARM64 + Entitlements.plist + Automatic:AppStore + iPhone Distribution + + + + + + + + + + + + + + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll + + + + + + ..\..\..\..\packages\xunit.runner.devices.2.3.3\lib\xamarinios10\xunit.runner.devices.dll + + + ..\..\..\..\packages\xunit.runner.devices.2.3.3\lib\xamarinios10\xunit.runner.utility.netstandard15.dll + + + ..\..\..\..\packages\xunit.abstractions.2.0.1\lib\netstandard1.0\xunit.abstractions.dll + + + ..\..\..\..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\..\..\..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\..\..\..\packages\xunit.extensibility.execution.2.3.1\lib\netstandard1.1\xunit.execution.dotnet.dll + + + ..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll + + + ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.dll + + + ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.iOS.dll + + + ..\..\..\..\packages\IdentityModel.3.0.0\lib\netstandard2.0\IdentityModel.dll + + + ..\..\..\..\packages\Acr.Support.2.1.0\lib\Xamarin.iOS10\Acr.Support.iOS.dll + + + ..\..\..\..\packages\BTProgressHUD.1.2.0.5\lib\Xamarin.iOS10\BTProgressHUD.dll + + + ..\..\..\..\packages\Splat.2.0.0\lib\Xamarin.iOS10\Splat.dll + + + ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.dll + + + ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.Interface.dll + + + ..\..\..\..\packages\WebP.Touch.1.0.7\lib\Xamarin.iOS10\WebP.Touch.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Platform.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.Touch.dll + + + ..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll + + + ..\..\..\..\packages\PInvoke.Kernel32.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Kernel32.dll + + + ..\..\..\..\packages\PInvoke.BCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.BCrypt.dll + + + ..\..\..\..\packages\PInvoke.NCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.NCrypt.dll + + + ..\..\..\..\packages\Validation.2.2.8\lib\dotnet\Validation.dll + + + ..\..\..\..\packages\PCLCrypto.2.0.147\lib\xamarinios10\PCLCrypto.dll + + + + + + + + {FDD910BC-DF0F-483D-B7D5-C7D831855172} + eShopOnContainers.UnitTests + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/Mocks/MockSettingsService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/Mocks/MockSettingsService.cs index 23e1cedc5..37bfc125f 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/Mocks/MockSettingsService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.UnitTests/Mocks/MockSettingsService.cs @@ -1,65 +1,110 @@ using eShopOnContainers.Core.Services.Settings; -using System; +using System.Collections.Generic; +using System.Threading.Tasks; namespace eShopOnContainers.UnitTests.Mocks { public class MockSettingsService : ISettingsService { - string _accessTokenDefault = string.Empty; - string _idTokenDefault = string.Empty; - bool _useMocksDefault = true; - string _urlBaseDefault = "https://13.88.8.119"; - bool _useFakeLocationDefault = false; - bool _allowGpsLocationDefault = false; - double _fakeLatitudeDefault = 47.604610d; - double _fakeLongitudeDefault = -122.315752d; + IDictionary _settings = new Dictionary(); + + const string AccessToken = "access_token"; + const string IdToken = "id_token"; + const string IdUseMocks = "use_mocks"; + const string IdUrlBase = "url_base"; + const string IdUseFakeLocation = "use_fake_location"; + const string IdLatitude = "latitude"; + const string IdLongitude = "longitude"; + const string IdAllowGpsLocation = "allow_gps_location"; + readonly string AccessTokenDefault = string.Empty; + readonly string IdTokenDefault = string.Empty; + readonly bool UseMocksDefault = true; + readonly bool UseFakeLocationDefault = false; + readonly bool AllowGpsLocationDefault = false; + readonly double FakeLatitudeDefault = 47.604610d; + readonly double FakeLongitudeDefault = -122.315752d; + readonly string UrlBaseDefault = "https://13.88.8.119"; public string AuthAccessToken { - get { return _accessTokenDefault; } - set { _accessTokenDefault = value; } + get => GetValueOrDefault(AccessToken, AccessTokenDefault); + set => AddOrUpdateValue(AccessToken, value); } public string AuthIdToken { - get { return _idTokenDefault; } - set { _idTokenDefault = value; } + get => GetValueOrDefault(IdToken, IdTokenDefault); + set => AddOrUpdateValue(IdToken, value); } public bool UseMocks { - get { return _useMocksDefault; } - set { _useMocksDefault = value; } + get => GetValueOrDefault(IdUseMocks, UseMocksDefault); + set => AddOrUpdateValue(IdUseMocks, value); } public string UrlBase { - get { return _urlBaseDefault; } - set { _urlBaseDefault = value; } + get => GetValueOrDefault(IdUrlBase, UrlBaseDefault); + set => AddOrUpdateValue(IdUrlBase, value); } public bool UseFakeLocation { - get { return _useFakeLocationDefault; } - set { _useFakeLocationDefault = value; } + get => GetValueOrDefault(IdUseFakeLocation, UseFakeLocationDefault); + set => AddOrUpdateValue(IdUseFakeLocation, value); } public string Latitude { - get { return _fakeLatitudeDefault.ToString(); } - set { _fakeLatitudeDefault = Convert.ToDouble(value); } + get => GetValueOrDefault(IdLatitude, FakeLatitudeDefault.ToString()); + set => AddOrUpdateValue(IdLatitude, value); } public string Longitude { - get { return _fakeLongitudeDefault.ToString(); } - set { _fakeLongitudeDefault = Convert.ToDouble(value); } + get => GetValueOrDefault(IdLongitude, FakeLongitudeDefault.ToString()); + set => AddOrUpdateValue(IdLongitude, value); } public bool AllowGpsLocation { - get { return _allowGpsLocationDefault; } - set { _allowGpsLocationDefault = value; } + get => GetValueOrDefault(IdAllowGpsLocation, AllowGpsLocationDefault); + set => AddOrUpdateValue(IdAllowGpsLocation, value); + } + + public Task AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); + public Task AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); + public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); + public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); + + Task AddOrUpdateValueInternal(string key, T value) + { + if (value == null) + { + Remove(key); + } + + _settings[key] = value; + return Task.Delay(10); + } + + T GetValueOrDefaultInternal(string key, T defaultValue = default(T)) + { + object value = null; + if (_settings.ContainsKey(key)) + { + value = _settings[key]; + } + return null != value ? (T)value : defaultValue; + } + + void Remove(string key) + { + if (_settings[key] != null) + { + _settings.Remove(key); + } } } } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Services/SettingsServiceImplementation.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Services/SettingsServiceImplementation.cs deleted file mode 100644 index e13fafd9f..000000000 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Services/SettingsServiceImplementation.cs +++ /dev/null @@ -1,99 +0,0 @@ -using eShopOnContainers.Core.Services.Settings; -using eShopOnContainers.Windows.Services; -using Windows.Storage; - -[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))] -namespace eShopOnContainers.Windows.Services -{ - public class SettingsServiceImplementation : ISettingsServiceImplementation - { - #region Internal Implementation - - readonly object _locker = new object(); - - ApplicationDataContainer GetAppSettings() - { - return ApplicationData.Current.LocalSettings; - } - - bool AddOrUpdateValueInternal(string key, T value) - { - bool valueChanged = false; - - if (value == null) - { - Remove(key); - return true; - } - - lock (_locker) - { - var settings = GetAppSettings(); - if (settings.Values.ContainsKey(key)) - { - if (settings.Values[key] != (object)value) - { - settings.Values[key] = value; - valueChanged = true; - } - } - else - { - settings.Values[key] = value; - valueChanged = true; - } - } - - return valueChanged; - } - - T GetValueOrDefaultInternal(string key, T defaultValue = default(T)) - { - object value; - - lock (_locker) - { - var settings = GetAppSettings(); - if (settings.Values.ContainsKey(key)) - { - var tempValue = settings.Values[key]; - if (tempValue != null) - value = (T)tempValue; - else - value = defaultValue; - } - else - { - value = defaultValue; - } - } - return null != value ? (T)value : defaultValue; - } - - #endregion - - #region ISettingsServiceImplementation - - public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); - - public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); - - public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); - - public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); - - public void Remove(string key) - { - lock (_locker) - { - var settings = GetAppSettings(); - if (settings.Values.ContainsKey(key)) - { - settings.Values.Remove(key); - } - } - } - - #endregion - } -} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj index b879d31c6..9a1a2d108 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj @@ -123,7 +123,6 @@ - diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/SettingsServiceImplementation.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/SettingsServiceImplementation.cs deleted file mode 100644 index ee17b8ff7..000000000 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/SettingsServiceImplementation.cs +++ /dev/null @@ -1,128 +0,0 @@ -using eShopOnContainers.Core.Services.Settings; -using eShopOnContainers.iOS.Services; -using Foundation; -using System; - -[assembly: Xamarin.Forms.Dependency(typeof(SettingsServiceImplementation))] -namespace eShopOnContainers.iOS.Services -{ - public class SettingsServiceImplementation : ISettingsServiceImplementation - { - #region Internal Implementation - - readonly object _locker = new object(); - - NSUserDefaults GetUserDefaults() => NSUserDefaults.StandardUserDefaults; - - bool AddOrUpdateValueInternal(string key, T value) - { - if (value == null) - { - Remove(key); - return true; - } - - var type = typeof(T); - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - type = Nullable.GetUnderlyingType(type); - } - var typeCode = Type.GetTypeCode(type); - - lock (_locker) - { - var defaults = GetUserDefaults(); - switch (typeCode) - { - case TypeCode.Boolean: - defaults.SetBool(Convert.ToBoolean(value), key); - break; - case TypeCode.String: - defaults.SetString(Convert.ToString(value), key); - break; - default: - throw new ArgumentException($"Value of type {typeCode} is unsupported."); - } - - try - { - defaults.Synchronize(); - } - catch (Exception ex) - { - Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message); - } - } - return true; - } - - T GetValueOrDefaultInternal(string key, T defaultValue = default(T)) - { - lock (_locker) - { - var defaults = GetUserDefaults(); - - if (defaults[key] == null) - { - return defaultValue; - } - - var type = typeof(T); - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - type = Nullable.GetUnderlyingType(type); - } - - object value = null; - var typeCode = Type.GetTypeCode(type); - switch (typeCode) - { - case TypeCode.Boolean: - value = defaults.BoolForKey(key); - break; - case TypeCode.String: - value = defaults.StringForKey(key); - break; - default: - throw new ArgumentException($"Value of type {typeCode} is unsupported."); - } - - return null != value ? (T)value : defaultValue; - } - } - - #endregion - - #region ISettingsServiceImplementation - - public bool AddOrUpdateValue(string key, bool value) => AddOrUpdateValueInternal(key, value); - - public bool AddOrUpdateValue(string key, string value) => AddOrUpdateValueInternal(key, value); - - public bool GetValueOrDefault(string key, bool defaultValue) => GetValueOrDefaultInternal(key, defaultValue); - - public string GetValueOrDefault(string key, string defaultValue) => GetValueOrDefaultInternal(key, defaultValue); - - public void Remove(string key) - { - lock (_locker) - { - var defaults = GetUserDefaults(); - try - { - if (defaults[key] != null) - { - defaults.RemoveObject(key); - defaults.Synchronize(); - } - } - catch (Exception ex) - { - Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message); - } - } - } - - #endregion - } -} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj index 6ea0a4465..3d18f3a5c 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/eShopOnContainers.iOS.csproj @@ -1,429 +1,428 @@  - - - Debug - iPhoneSimulator - 8.0.30703 - 2.0 - {6EEB23DC-7063-4444-9AF8-90DF24F549C0} - {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Exe - eShopOnContainers.iOS - Resources - eShopOnContainersiOS - - - true - - - true - full - false - bin\iPhoneSimulator\Debug - DEBUG - prompt - 4 - false - i386, x86_64 - None - True - False - False - False - False - False - True - Default - HttpClientHandler - False - - - none - true - bin\iPhoneSimulator\Release - prompt - 4 - None - i386, x86_64 - false - - - true - full - false - bin\iPhone\Debug - DEBUG - prompt - 4 - false - ARMv7, ARM64 - iPhone Developer - true - Entitlements.plist - None - - - none - true - bin\iPhone\Release - prompt - 4 - ARMv7, ARM64 - false - iPhone Developer - Entitlements.plist - - - none - True - bin\iPhone\Ad-Hoc - prompt - 4 - False - ARMv7, ARM64 - True - Automatic:AdHoc - iPhone Distribution - Entitlements.plist - - - none - True - bin\iPhone\AppStore - prompt - 4 - False - ARMv7, ARM64 - Automatic:AppStore - iPhone Distribution - Entitlements.plist - - - - - - - Resources\fonts\Montserrat-Bold.ttf - - - Resources\fonts\Montserrat-Regular.ttf - - - Resources\fonts\SourceSansPro-Regular.ttf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll - - - ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll - - - ..\..\..\..\packages\WebP.Touch.1.0.7\lib\Xamarin.iOS10\WebP.Touch.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Platform.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.dll - - - ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.Touch.dll - - - ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.dll - - - ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.iOS.dll - - - ..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll - - - ..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\Xamarin.iOS10\Plugin.Settings.dll - - - - ..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll - - - ..\..\..\..\packages\Acr.Support.2.1.0\lib\Xamarin.iOS10\Acr.Support.iOS.dll - - - ..\..\..\..\packages\BTProgressHUD.1.2.0.5\lib\Xamarin.iOS10\BTProgressHUD.dll - - - ..\..\..\..\packages\Splat.2.0.0\lib\Xamarin.iOS10\Splat.dll - - - ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.dll - - - ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.Interface.dll - - - ..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll - - - ..\..\..\..\packages\PInvoke.Kernel32.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Kernel32.dll - - - ..\..\..\..\packages\PInvoke.BCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.BCrypt.dll - - - ..\..\..\..\packages\PInvoke.NCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.NCrypt.dll - - - ..\..\..\..\packages\Validation.2.2.8\lib\dotnet\Validation.dll - - - ..\..\..\..\packages\PCLCrypto.2.0.147\lib\xamarinios10\PCLCrypto.dll - - - ..\..\..\..\packages\IdentityModel.3.0.0\lib\netstandard2.0\IdentityModel.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {76C5F2A7-6CD5-49EA-9F33-EC44DE6539C7} - eShopOnContainers.Core - - - - - - - - - + - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + Debug + iPhoneSimulator + 8.0.30703 + 2.0 + {6EEB23DC-7063-4444-9AF8-90DF24F549C0} + {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Exe + eShopOnContainers.iOS + Resources + eShopOnContainersiOS + + + true + true - - - + + true + full + false + bin\iPhoneSimulator\Debug + DEBUG + prompt + 4 + false + i386, x86_64 + None + True + False + False + False + False + False + True + Default + HttpClientHandler + False + + + none + true + bin\iPhoneSimulator\Release + prompt + 4 + None + i386, x86_64 + false + + + true + full + false + bin\iPhone\Debug + DEBUG + prompt + 4 + false + ARMv7, ARM64 + iPhone Developer + true + Entitlements.plist + None + + + none + true + bin\iPhone\Release + prompt + 4 + ARMv7, ARM64 + false + iPhone Developer + Entitlements.plist + + + none + True + bin\iPhone\Ad-Hoc + prompt + 4 + False + ARMv7, ARM64 + True + Automatic:AdHoc + iPhone Distribution + Entitlements.plist + + + none + True + bin\iPhone\AppStore + prompt + 4 + False + ARMv7, ARM64 + Automatic:AppStore + iPhone Distribution + Entitlements.plist + + + + + + + Resources\fonts\Montserrat-Bold.ttf + + + Resources\fonts\Montserrat-Regular.ttf + + + Resources\fonts\SourceSansPro-Regular.ttf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll + + + ..\..\..\..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll + + + ..\..\..\..\packages\WebP.Touch.1.0.7\lib\Xamarin.iOS10\WebP.Touch.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Platform.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.dll + + + ..\..\..\..\packages\Xamarin.FFImageLoading.Forms.2.3.4\lib\Xamarin.iOS10\FFImageLoading.Forms.Touch.dll + + + ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.dll + + + ..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.iOS.dll + + + ..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll + + + ..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\Xamarin.iOS10\Plugin.Settings.dll + + + ..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll + + + ..\..\..\..\packages\Acr.Support.2.1.0\lib\Xamarin.iOS10\Acr.Support.iOS.dll + + + ..\..\..\..\packages\BTProgressHUD.1.2.0.5\lib\Xamarin.iOS10\BTProgressHUD.dll + + + ..\..\..\..\packages\Splat.2.0.0\lib\Xamarin.iOS10\Splat.dll + + + ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.dll + + + ..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.Interface.dll + + + ..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll + + + ..\..\..\..\packages\PInvoke.Kernel32.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Kernel32.dll + + + ..\..\..\..\packages\PInvoke.BCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.BCrypt.dll + + + ..\..\..\..\packages\PInvoke.NCrypt.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.NCrypt.dll + + + ..\..\..\..\packages\Validation.2.2.8\lib\dotnet\Validation.dll + + + ..\..\..\..\packages\PCLCrypto.2.0.147\lib\xamarinios10\PCLCrypto.dll + + + ..\..\..\..\packages\IdentityModel.3.0.0\lib\netstandard2.0\IdentityModel.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {76C5F2A7-6CD5-49EA-9F33-EC44DE6539C7} + eShopOnContainers.Core + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index 11ecac7c2..48ad68903 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -34,6 +34,10 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers public async Task Get(string id) { var basket = await _repository.GetBasketAsync(id); + if (basket == null) + { + return NotFound(); + } return Ok(basket); } diff --git a/src/Services/Basket/Basket.API/Properties/launchSettings.json b/src/Services/Basket/Basket.API/Properties/launchSettings.json index 36effc287..60a56b153 100644 --- a/src/Services/Basket/Basket.API/Properties/launchSettings.json +++ b/src/Services/Basket/Basket.API/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:5103/", + "applicationUrl": "http://localhost:58017/", "sslPort": 0 } }, @@ -19,7 +19,7 @@ "Microsoft.eShopOnContainers.Services.Basket.API": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "http://localhost:5000/swagger", + "launchUrl": "http://localhost:55103/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 1b53171d0..71f700762 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -32,11 +32,16 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] [HttpGet] - [Route("[action]")] + [Route("items")] [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] - public async Task Items([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0) - + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task Items([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0, [FromQuery] string ids = null) { + if (!string.IsNullOrEmpty(ids)) + { + return GetItemsByIds(ids); + } + var totalItems = await _catalogContext.CatalogItems .LongCountAsync(); @@ -54,6 +59,23 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers return Ok(model); } + private IActionResult GetItemsByIds(string ids) + { + var numIds = ids.Split(',') + .Select(id => (Ok: int.TryParse(id, out int x), Value: x)); + if (!numIds.All(nid => nid.Ok)) + { + return BadRequest("ids value invalid. Must be comma-separated list of numbers"); + } + + var idsToSelect = numIds.Select(id => id.Value); + var items = _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToList(); + + items = ChangeUriPlaceholder(items); + return Ok(items); + + } + [HttpGet] [Route("items/{id:int}")] [ProducesResponseType((int)HttpStatusCode.NotFound)] @@ -66,6 +88,11 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers } var item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(ci => ci.Id == id); + + var baseUri = _settings.PicBaseUrl; + var azureStorageEnabled = _settings.AzureStorageEnabled; + item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); + if (item != null) { return Ok(item); @@ -244,13 +271,12 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers private List ChangeUriPlaceholder(List items) { var baseUri = _settings.PicBaseUrl; + var azureStorageEnabled = _settings.AzureStorageEnabled; - items.ForEach(catalogItem => + foreach (var item in items) { - catalogItem.PictureUri = _settings.AzureStorageEnabled - ? baseUri + catalogItem.PictureFileName - : baseUri.Replace("[0]", catalogItem.Id.ToString()); - }); + item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); + } return items; } diff --git a/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs b/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs new file mode 100644 index 000000000..71b9d88a3 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +{ + public static class CatalogItemExtensions + { + public static void FillProductUrl(this CatalogItem item, string picBaseUrl, bool azureStorageEnabled) + { + item.PictureUri = azureStorageEnabled + ? picBaseUrl + item.PictureFileName + : picBaseUrl.Replace("[0]", item.Id.ToString()); + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Properties/launchSettings.json b/src/Services/Catalog/Catalog.API/Properties/launchSettings.json index 1bf8eb1f3..2b21ca280 100644 --- a/src/Services/Catalog/Catalog.API/Properties/launchSettings.json +++ b/src/Services/Catalog/Catalog.API/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:5101", + "applicationUrl": "http://localhost:57424/", "sslPort": 0 } }, @@ -19,6 +19,7 @@ "Microsoft.eShopOnContainers.Services.Catalog.API": { "commandName": "Project", "launchBrowser": true, + "launchUrl": "http://localhost:55101/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Services/Identity/Identity.API/Configuration/Config.cs b/src/Services/Identity/Identity.API/Configuration/Config.cs index 969b86aa1..3bc705995 100644 --- a/src/Services/Identity/Identity.API/Configuration/Config.cs +++ b/src/Services/Identity/Identity.API/Configuration/Config.cs @@ -14,7 +14,9 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration new ApiResource("orders", "Orders Service"), new ApiResource("basket", "Basket Service"), new ApiResource("marketing", "Marketing Service"), - new ApiResource("locations", "Locations Service") + new ApiResource("locations", "Locations Service"), + new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"), + new ApiResource("webshoppingagg", "Web Shopping Aggregator") }; } @@ -52,7 +54,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration "orders", "basket", "locations", - "marketing" + "marketing", + "webshoppingagg" } }, new Client @@ -78,7 +81,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration "orders", "basket", "locations", - "marketing" + "marketing", + "mobileshoppingagg" }, //Allow requesting refresh tokens for long lived API access AllowOfflineAccess = true, @@ -114,7 +118,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration "orders", "basket", "locations", - "marketing" + "marketing", + "webshoppingagg" }, }, new Client @@ -146,7 +151,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration "orders", "basket", "locations", - "marketing" + "marketing", + "webshoppingagg" }, }, new Client @@ -208,7 +214,38 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration { "orders" } + }, + new Client + { + ClientId = "mobileshoppingaggswaggerui", + ClientName = "Mobile Shopping Aggregattor Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/o2c.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/" }, + + AllowedScopes = + { + "mobileshoppingagg" + } + }, + new Client + { + ClientId = "webshoppingaggswaggerui", + ClientName = "Web Shopping Aggregattor Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/o2c.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/" }, + + AllowedScopes = + { + "webshoppingagg" + } } + }; } } diff --git a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs index b109bf896..a5a4383bd 100644 --- a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs @@ -1,7 +1,6 @@ using IdentityServer4.EntityFramework.DbContexts; using IdentityServer4.EntityFramework.Mappers; using Microsoft.eShopOnContainers.Services.Identity.API.Configuration; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using System.Collections.Generic; using System.Linq; @@ -11,36 +10,49 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data { public class ConfigurationDbContextSeed { - public async Task SeedAsync(ConfigurationDbContext context, IConfiguration configuration) + public async Task SeedAsync(ConfigurationDbContext context,IConfiguration configuration) { + //callbacks urls from config: - var clientUrls = new Dictionary - { - {"Mvc", configuration.GetValue("MvcClient")}, - {"Spa", configuration.GetValue("SpaClient")}, - {"Xamarin", configuration.GetValue("XamarinCallback")}, - {"LocationsApi", configuration.GetValue("LocationApiClient")}, - {"MarketingApi", configuration.GetValue("MarketingApiClient")}, - {"BasketApi", configuration.GetValue("BasketApiClient")}, - {"OrderingApi", configuration.GetValue("OrderingApiClient")} - }; + var clientUrls = new Dictionary(); + + clientUrls.Add("Mvc", configuration.GetValue("MvcClient")); + clientUrls.Add("Spa", configuration.GetValue("SpaClient")); + clientUrls.Add("Xamarin", configuration.GetValue("XamarinCallback")); + clientUrls.Add("LocationsApi", configuration.GetValue("LocationApiClient")); + clientUrls.Add("MarketingApi", configuration.GetValue("MarketingApiClient")); + clientUrls.Add("BasketApi", configuration.GetValue("BasketApiClient")); + clientUrls.Add("OrderingApi", configuration.GetValue("OrderingApiClient")); + clientUrls.Add("MobileShoppingAgg", configuration.GetValue("MobileShoppingAggClient")); + clientUrls.Add("WebShoppingAgg", configuration.GetValue("WebShoppingAggClient")); - if (!await context.Clients.AnyAsync()) + if (!context.Clients.Any()) { - context.Clients.AddRange(Config.GetClients(clientUrls).Select(client => client.ToEntity())); + foreach (var client in Config.GetClients(clientUrls)) + { + await context.Clients.AddAsync(client.ToEntity()); + } + await context.SaveChangesAsync(); } - if (!await context.IdentityResources.AnyAsync()) + if (!context.IdentityResources.Any()) { - context.IdentityResources.AddRange(Config.GetResources().Select(resource => resource.ToEntity())); + foreach (var resource in Config.GetResources()) + { + await context.IdentityResources.AddAsync(resource.ToEntity()); + } + await context.SaveChangesAsync(); } - if (!await context.ApiResources.AnyAsync()) + if (!context.ApiResources.Any()) { - context.ApiResources.AddRange(Config.GetApis().Select(api => api.ToEntity())); - } + foreach (var api in Config.GetApis()) + { + await context.ApiResources.AddAsync(api.ToEntity()); + } - await context.SaveChangesAsync(); + await context.SaveChangesAsync(); + } } } } diff --git a/src/Services/Identity/Identity.API/Properties/launchSettings.json b/src/Services/Identity/Identity.API/Properties/launchSettings.json index 91f06fd57..e52e9f99c 100644 --- a/src/Services/Identity/Identity.API/Properties/launchSettings.json +++ b/src/Services/Identity/Identity.API/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:5105", + "applicationUrl": "http://localhost:54010/", "sslPort": 0 } }, @@ -11,7 +11,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "http://localhost:5105", + "launchUrl": "http://localhost:55105", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -19,7 +19,7 @@ "eShopOnContainers.Identity": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "http://localhost:5000", + "launchUrl": "http://localhost:55105", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Services/Identity/Identity.API/Services/IEmailSender.cs b/src/Services/Identity/Identity.API/Services/IEmailSender.cs deleted file mode 100644 index 95a4f99ee..000000000 --- a/src/Services/Identity/Identity.API/Services/IEmailSender.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Identity.API.Services -{ - public interface IEmailSender - { - Task SendEmailAsync(string email, string subject, string message); - } -} diff --git a/src/Services/Identity/Identity.API/Services/ISmsSender.cs b/src/Services/Identity/Identity.API/Services/ISmsSender.cs deleted file mode 100644 index f842e7ae9..000000000 --- a/src/Services/Identity/Identity.API/Services/ISmsSender.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Identity.API.Services -{ - public interface ISmsSender - { - Task SendSmsAsync(string number, string message); - } -} diff --git a/src/Services/Identity/Identity.API/Services/MessageServices.cs b/src/Services/Identity/Identity.API/Services/MessageServices.cs deleted file mode 100644 index 18ab35a7c..000000000 --- a/src/Services/Identity/Identity.API/Services/MessageServices.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Identity.API.Services -{ - // This class is used by the application to send Email and SMS - // when you turn on two-factor authentication in ASP.NET Identity. - // For more details see this link http://go.microsoft.com/fwlink/?LinkID=532713 - public class AuthMessageSender : IEmailSender, ISmsSender - { - public Task SendEmailAsync(string email, string subject, string message) - { - // Plug in your email service here to send an email. - return Task.FromResult(0); - } - - public Task SendSmsAsync(string number, string message) - { - // Plug in your SMS service here to send a text message. - return Task.FromResult(0); - } - } -} diff --git a/src/Services/Identity/Identity.API/Startup.cs b/src/Services/Identity/Identity.API/Startup.cs index f16f90965..f564043ae 100644 --- a/src/Services/Identity/Identity.API/Startup.cs +++ b/src/Services/Identity/Identity.API/Startup.cs @@ -73,8 +73,6 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API checks.AddSqlCheck("Identity_Db", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); }); - services.AddTransient(); - services.AddTransient(); services.AddTransient, EFLoginService>(); services.AddTransient(); diff --git a/src/Services/Location/Locations.API/Properties/launchSettings.json b/src/Services/Location/Locations.API/Properties/launchSettings.json index 45b637914..1c86a33b1 100644 --- a/src/Services/Location/Locations.API/Properties/launchSettings.json +++ b/src/Services/Location/Locations.API/Properties/launchSettings.json @@ -1,9 +1,9 @@ -{ +{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:3278/", + "applicationUrl": "http://localhost:54020/", "sslPort": 0 } }, @@ -26,4 +26,4 @@ "applicationUrl": "http://localhost:3279" } } -} +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Properties/launchSettings.json b/src/Services/Marketing/Marketing.API/Properties/launchSettings.json index e222e065d..dea046099 100644 --- a/src/Services/Marketing/Marketing.API/Properties/launchSettings.json +++ b/src/Services/Marketing/Marketing.API/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:5110", + "applicationUrl": "http://localhost:63455/", "sslPort": 0 } }, diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs index 980f9d0b6..437212b8d 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Runtime.Serialization; using System.Collections; using Ordering.API.Application.Models; +using System.Linq; namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands { @@ -68,7 +69,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands string cardNumber, string cardHolderName, DateTime cardExpiration, string cardSecurityNumber, int cardTypeId) : this() { - _orderItems = MapToOrderItems(basketItems); + _orderItems = basketItems.ToOrderItemsDTO().ToList(); UserId = userId; City = city; Street = street; @@ -83,20 +84,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands CardExpiration = cardExpiration; } - private List MapToOrderItems(List basketItems) - { - var result = new List(); - basketItems.ForEach((item) => { - result.Add(new OrderItemDTO() { - ProductId = int.TryParse(item.ProductId, out int id) ? id : -1, - ProductName = item.ProductName, - PictureUrl = item.PictureUrl, - UnitPrice = item.UnitPrice, - Units = item.Quantity - }); - }); - return result; - } public class OrderItemDTO { diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs new file mode 100644 index 000000000..1d3d52c63 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs @@ -0,0 +1,27 @@ +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Ordering.API.Application.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; + +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands +{ + public class CreateOrderDraftCommand : IRequest + { + + public string BuyerId { get; private set; } + + public IEnumerable Items { get; private set; } + + public CreateOrderDraftCommand(string buyerId, IEnumerable items) + { + BuyerId = buyerId; + Items = items; + } + } + +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs new file mode 100644 index 000000000..2c315248b --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs @@ -0,0 +1,72 @@ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands +{ + using Domain.AggregatesModel.OrderAggregate; + using global::Ordering.API.Application.Models; + using MediatR; + using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; + using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; + + // Regular CommandHandler + public class CreateOrderDraftCommandHandler + : IRequestHandler + { + private readonly IOrderRepository _orderRepository; + private readonly IIdentityService _identityService; + private readonly IMediator _mediator; + + // Using DI to inject infrastructure persistence Repositories + public CreateOrderDraftCommandHandler(IMediator mediator, IIdentityService identityService) + { + _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + } + + public Task Handle(CreateOrderDraftCommand message, CancellationToken cancellationToken) + { + + var order = Order.NewDraft(); + var orderItems = message.Items.Select(i => i.ToOrderItemDTO()); + foreach (var item in orderItems) + { + order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); + } + + return Task.FromResult(OrderDraftDTO.FromOrder(order)); + } + } + + + public class OrderDraftDTO + { + public IEnumerable OrderItems { get; set; } + public decimal Total { get; set; } + + public static OrderDraftDTO FromOrder(Order order) + { + return new OrderDraftDTO() + { + OrderItems = order.OrderItems.Select(oi => new OrderItemDTO + { + Discount = oi.GetCurrentDiscount(), + ProductId = oi.ProductId, + UnitPrice = oi.GetUnitPrice(), + PictureUrl = oi.GetPictureUri(), + Units = oi.GetUnits(), + ProductName = oi.GetOrderItemProductName() + }), + Total = order.GetTotal() + }; + } + + } + + + + +} diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index 993986543..ac179c97f 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -5,6 +5,7 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; using Ordering.API.Application.Commands; +using Ordering.API.Application.Models; using System; using System.Collections.Generic; using System.Net; @@ -101,6 +102,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers return Ok(cardTypes); } + + [Route("draft")] + [HttpPost] + public async Task GetOrderDraftFromBasketData([FromBody] CreateOrderDraftCommand createOrderDraftCommand) + { + var draft = await _mediator.Send(createOrderDraftCommand); + return Ok(draft); + } } } diff --git a/src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs b/src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs new file mode 100644 index 000000000..56f280978 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs @@ -0,0 +1,32 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; + +namespace Ordering.API.Application.Models +{ + public static class BasketItemExtensions + { + public static IEnumerable ToOrderItemsDTO(this IEnumerable basketItems) + { + foreach (var item in basketItems) + { + yield return item.ToOrderItemDTO(); + } + } + + public static OrderItemDTO ToOrderItemDTO(this BasketItem item) + { + return new OrderItemDTO() + { + ProductId = int.TryParse(item.ProductId, out int id) ? id : -1, + ProductName = item.ProductName, + PictureUrl = item.PictureUrl, + UnitPrice = item.UnitPrice, + Units = item.Quantity + }; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Properties/launchSettings.json b/src/Services/Ordering/Ordering.API/Properties/launchSettings.json index a6e47ca2d..9d9a76490 100644 --- a/src/Services/Ordering/Ordering.API/Properties/launchSettings.json +++ b/src/Services/Ordering/Ordering.API/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:5102/", + "applicationUrl": "http://localhost:55102/", "sslPort": 0 } }, @@ -19,7 +19,7 @@ "Microsoft.eShopOnContainers.Services.Ordering.API": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "http://localhost:5000/api/environmentInfo/machinename", + "launchUrl": "http://localhost:55102/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs index 428d97580..82e5744dd 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs @@ -25,6 +25,10 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O private string _description; + + // Draft orders have this set to true. Currently we don't check anywhere the draft status of an Order, but we could do it if needed + private bool _isDraft; + // DDD Patterns comment // Using a private collection field, better for DDD Aggregate's encapsulation // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, @@ -34,12 +38,21 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O private int? _paymentMethodId; - protected Order() { _orderItems = new List(); } + public static Order NewDraft() + { + var order = new Order(); + order._isDraft = true; + return order; + } + + protected Order() { + _orderItems = new List(); + _isDraft = false; + } public Order(string userId, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber, - string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) + string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this() { - _orderItems = new List(); _buyerId = buyerId; _paymentMethodId = paymentMethodId; _orderStatusId = OrderStatus.Submitted.Id; @@ -92,35 +105,34 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O } public void SetAwaitingValidationStatus() - { + { if (_orderStatusId == OrderStatus.Submitted.Id) { - _orderStatusId = OrderStatus.AwaitingValidation.Id; - AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems)); - } + _orderStatusId = OrderStatus.AwaitingValidation.Id; + } } public void SetStockConfirmedStatus() { if (_orderStatusId == OrderStatus.AwaitingValidation.Id) { + AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id)); + _orderStatusId = OrderStatus.StockConfirmed.Id; _description = "All the items were confirmed with available stock."; - - AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id)); - } + } } public void SetPaidStatus() { if (_orderStatusId == OrderStatus.StockConfirmed.Id) { + AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems)); + _orderStatusId = OrderStatus.Paid.Id; _description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\""; - - AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems)); - } + } } public void SetShippedStatus() @@ -158,13 +170,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames); _description = $"The product items don't have stock: ({itemsStockRejectedDescription})."; - } + } } private void AddOrderStartedDomainEvent(string userId, int cardTypeId, string cardNumber, string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) { - var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, cardTypeId, + var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, cardTypeId, cardNumber, cardSecurityNumber, cardHolderName, cardExpiration); diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs index 1b7896dcc..b3b7435e3 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs @@ -41,13 +41,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O _pictureUrl = PictureUrl; } - public void SetPictureUri(string pictureUri) - { - if (!String.IsNullOrWhiteSpace(pictureUri)) - { - _pictureUrl = pictureUri; - } - } + public string GetPictureUri() => _pictureUrl; public decimal GetCurrentDiscount() { diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs index 11b940218..a1f2fa1c8 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs @@ -21,17 +21,22 @@ } private List _domainEvents; - public List DomainEvents => _domainEvents; - + public IReadOnlyCollection DomainEvents => _domainEvents?.AsReadOnly(); + public void AddDomainEvent(INotification eventItem) { _domainEvents = _domainEvents ?? new List(); _domainEvents.Add(eventItem); } + public void RemoveDomainEvent(INotification eventItem) { - if (_domainEvents is null) return; - _domainEvents.Remove(eventItem); + _domainEvents?.Remove(eventItem); + } + + public void ClearDomainEvents() + { + _domainEvents?.Clear(); } public bool IsTransient() diff --git a/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs b/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs index 521f85ae8..82a7235a2 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs @@ -19,7 +19,7 @@ namespace Ordering.Infrastructure .ToList(); domainEntities.ToList() - .ForEach(entity => entity.Entity.DomainEvents.Clear()); + .ForEach(entity => entity.Entity.ClearDomainEvents()); var tasks = domainEvents .Select(async (domainEvent) => { diff --git a/src/Services/Payment/Payment.API/Properties/launchSettings.json b/src/Services/Payment/Payment.API/Properties/launchSettings.json index faf310f83..5eac4c092 100644 --- a/src/Services/Payment/Payment.API/Properties/launchSettings.json +++ b/src/Services/Payment/Payment.API/Properties/launchSettings.json @@ -1,9 +1,9 @@ -{ +{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:3330/", + "applicationUrl": "http://localhost:63336/", "sslPort": 0 } }, @@ -26,4 +26,4 @@ "applicationUrl": "http://localhost:3331" } } -} +} \ No newline at end of file diff --git a/src/Web/WebMVC/AppSettings.cs b/src/Web/WebMVC/AppSettings.cs index 42100ab62..1f11e750e 100644 --- a/src/Web/WebMVC/AppSettings.cs +++ b/src/Web/WebMVC/AppSettings.cs @@ -8,11 +8,9 @@ namespace Microsoft.eShopOnContainers.WebMVC public class AppSettings { public Connectionstrings ConnectionStrings { get; set; } - public string CatalogUrl { get; set; } - public string OrderingUrl { get; set; } - public string BasketUrl { get; set; } public string MarketingUrl { get; set; } - public string LocationsUrl { get; set; } + + public string PurchaseUrl { get; set; } public bool ActivateCampaignDetailFunction { get; set; } public Logging Logging { get; set; } public bool UseCustomizationData { get; set; } diff --git a/src/Web/WebMVC/Controllers/CartController.cs b/src/Web/WebMVC/Controllers/CartController.cs index 8b41c5320..660da1d56 100644 --- a/src/Web/WebMVC/Controllers/CartController.cs +++ b/src/Web/WebMVC/Controllers/CartController.cs @@ -49,11 +49,8 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers { var user = _appUserParser.Parse(HttpContext.User); var basket = await _basketSvc.SetQuantities(user, quantities); - var vm = await _basketSvc.UpdateBasket(basket); - if (action == "[ Checkout ]") { - var order = _basketSvc.MapBasketToOrder(basket); return RedirectToAction("Create", "Order"); } } @@ -70,19 +67,11 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers { try { - if (productDetails.Id != null) + if (productDetails?.Id != null) { var user = _appUserParser.Parse(HttpContext.User); - var product = new BasketItem() - { - Id = Guid.NewGuid().ToString(), - Quantity = 1, - ProductName = productDetails.Name, - PictureUrl = productDetails.PictureUri, - UnitPrice = productDetails.Price, - ProductId = productDetails.Id - }; - await _basketSvc.AddItemToBasket(user, product); + await _basketSvc.AddItemToBasket(user, productDetails.Id); + //await _basketSvc.AddItemToBasket(user, product); } return RedirectToAction("Index", "Catalog"); } diff --git a/src/Web/WebMVC/Controllers/OrderController.cs b/src/Web/WebMVC/Controllers/OrderController.cs index 11e688728..a5bf4785e 100644 --- a/src/Web/WebMVC/Controllers/OrderController.cs +++ b/src/Web/WebMVC/Controllers/OrderController.cs @@ -27,9 +27,9 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers public async Task Create() { + var user = _appUserParser.Parse(HttpContext.User); - var basket = await _basketSvc.GetBasket(user); - var order = _basketSvc.MapBasketToOrder(basket); + var order = await _basketSvc.GetOrderDraft(user.Id); var vm = _orderSvc.MapUserInfoIntoOrder(user, order); vm.CardExpirationShortFormat(); diff --git a/src/Web/WebMVC/Controllers/TestController.cs b/src/Web/WebMVC/Controllers/TestController.cs new file mode 100644 index 000000000..1b35d5d2b --- /dev/null +++ b/src/Web/WebMVC/Controllers/TestController.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.eShopOnContainers.WebMVC.Services; +using Microsoft.eShopOnContainers.WebMVC.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebMVC.Controllers +{ + class TestPayload + { + public int CatalogItemId { get; set; } + public string BasketId { get; set; } + + public int Quantity { get; set; } + } + + [Authorize] + public class TestController : Controller + { + private readonly IHttpClient _client; + private readonly IIdentityParser _appUserParser; + public TestController(IHttpClient client, IIdentityParser identityParser) + { + _client = client; + _appUserParser = identityParser; + } + + public async Task Ocelot() + { + var url = "http://apigw/shopping/api/v1/basket/items"; + var payload = new TestPayload() + { + CatalogItemId = 1, + Quantity = 1, + BasketId = _appUserParser.Parse(User).Id + }; + var token = await HttpContext.GetTokenAsync("access_token"); + var response = await _client.PostAsync(url, payload, token); + + if (response.IsSuccessStatusCode) + { + var str = await response.Content.ReadAsStringAsync(); + return Ok(str); + } + else + { + return Ok(new { response.StatusCode, response.ReasonPhrase }); + } + } + } +} diff --git a/src/Web/WebMVC/Infrastructure/API.cs b/src/Web/WebMVC/Infrastructure/API.cs index edb2c5ed5..d6f485e21 100644 --- a/src/Web/WebMVC/Infrastructure/API.cs +++ b/src/Web/WebMVC/Infrastructure/API.cs @@ -4,27 +4,21 @@ namespace WebMVC.Infrastructure { public static class API { - public static class Basket - { - public static string GetBasket(string baseUri, string basketId) - { - return $"{baseUri}/{basketId}"; - } - public static string UpdateBasket(string baseUri) - { - return baseUri; - } + public static class Purchase + { + public static string AddItemToBasket(string baseUri) => $"{baseUri}/basket/items"; + public static string UpdateBasketItem(string baseUri) => $"{baseUri}/basket/items"; - public static string CheckoutBasket(string baseUri) - { - return $"{baseUri}/checkout"; - } + public static string GetOrderDraft(string baseUri, string basketId) => $"{baseUri}/order/draft/{basketId}"; + } - public static string CleanBasket(string baseUri, string basketId) - { - return $"{baseUri}/{basketId}"; - } + public static class Basket + { + public static string GetBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}"; + public static string UpdateBasket(string baseUri) => baseUri; + public static string CheckoutBasket(string baseUri) => $"{baseUri}/checkout"; + public static string CleanBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}"; } public static class Order @@ -100,7 +94,7 @@ namespace WebMVC.Infrastructure public static string CreateOrUpdateUserLocation(string baseUri) { return baseUri; - } + } } } } \ No newline at end of file diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index 6c8524800..54287a796 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -5,6 +5,7 @@ using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.Extensions.Options; using Newtonsoft.Json; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using WebMVC.Infrastructure; using WebMVC.Models; @@ -14,14 +15,19 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public class BasketService : IBasketService { private readonly IOptionsSnapshot _settings; - private IHttpClient _apiClient; - private readonly string _remoteServiceBaseUrl; - private IHttpContextAccessor _httpContextAccesor; + private readonly IHttpClient _apiClient; + private readonly string _basketByPassUrl; + private readonly string _purchaseUrl; + private readonly IHttpContextAccessor _httpContextAccesor; - public BasketService(IOptionsSnapshot settings, IHttpContextAccessor httpContextAccesor, IHttpClient httpClient) + private readonly string _bffUrl; + + public BasketService(IOptionsSnapshot settings, + IHttpContextAccessor httpContextAccesor, IHttpClient httpClient) { _settings = settings; - _remoteServiceBaseUrl = $"{_settings.Value.BasketUrl}/api/v1/basket"; + _basketByPassUrl = $"{_settings.Value.PurchaseUrl}/api/v1/b/basket"; + _purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1"; _httpContextAccesor = httpContextAccesor; _apiClient = httpClient; } @@ -29,24 +35,19 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public async Task GetBasket(ApplicationUser user) { var token = await GetUserTokenAsync(); - var getBasketUri = API.Basket.GetBasket(_remoteServiceBaseUrl, user.Id); + var getBasketUri = API.Basket.GetBasket(_basketByPassUrl, user.Id); var dataString = await _apiClient.GetStringAsync(getBasketUri, token); - // Use the ?? Null conditional operator to simplify the initialization of response - var response = JsonConvert.DeserializeObject(dataString) ?? - new Basket() - { - BuyerId = user.Id - }; - - return response; + return string.IsNullOrEmpty(dataString) ? + new Basket() { BuyerId = user.Id} : + JsonConvert.DeserializeObject(dataString); } public async Task UpdateBasket(Basket basket) { var token = await GetUserTokenAsync(); - var updateBasketUri = API.Basket.UpdateBasket(_remoteServiceBaseUrl); + var updateBasketUri = API.Basket.UpdateBasket(_basketByPassUrl); var response = await _apiClient.PostAsync(updateBasketUri, basket, token); @@ -58,7 +59,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public async Task Checkout(BasketDTO basket) { var token = await GetUserTokenAsync(); - var updateBasketUri = API.Basket.CheckoutBasket(_remoteServiceBaseUrl); + var updateBasketUri = API.Basket.CheckoutBasket(_basketByPassUrl); var response = await _apiClient.PostAsync(updateBasketUri, basket, token); @@ -67,60 +68,50 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public async Task SetQuantities(ApplicationUser user, Dictionary quantities) { - var basket = await GetBasket(user); - basket.Items.ForEach(x => + var token = await GetUserTokenAsync(); + var updateBasketUri = API.Purchase.UpdateBasketItem(_purchaseUrl); + var userId = user.Id; + + var response = await _apiClient.PutAsync(updateBasketUri, new { - // Simplify this logic by using the - // new out variable initializer. - if (quantities.TryGetValue(x.Id, out var quantity)) + BasketId = userId, + Updates = quantities.Select(kvp => new { - x.Quantity = quantity; - } - }); + BasketItemId = kvp.Key, + NewQty = kvp.Value + }).ToArray() + }, token); - return basket; + response.EnsureSuccessStatusCode(); + var jsonResponse = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(jsonResponse); } - public Order MapBasketToOrder(Basket basket) + public async Task GetOrderDraft(string basketId) { - var order = new Order(); - order.Total = 0; - - basket.Items.ForEach(x => - { - order.OrderItems.Add(new OrderItem() - { - ProductId = int.Parse(x.ProductId), + var token = await GetUserTokenAsync(); + var draftOrderUri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId); + var json = await _apiClient.GetStringAsync(draftOrderUri, token); + return JsonConvert.DeserializeObject(json); + } - PictureUrl = x.PictureUrl, - ProductName = x.ProductName, - Units = x.Quantity, - UnitPrice = x.UnitPrice - }); - order.Total += (x.Quantity * x.UnitPrice); - }); - return order; - } - public async Task AddItemToBasket(ApplicationUser user, BasketItem product) + public async Task AddItemToBasket(ApplicationUser user, int productId) { - var basket = await GetBasket(user); + var token = await GetUserTokenAsync(); + var updateBasketUri = API.Purchase.AddItemToBasket(_purchaseUrl); + var userId = user.Id; - if (basket == null) + var response = await _apiClient.PostAsync(updateBasketUri, new { - basket = new Basket() - { - BuyerId = user.Id, - Items = new List() - }; - } + CatalogItemId = productId, + BasketId = userId, + Quantity = 1 + }, token); - basket.Items.Add(product); - - await UpdateBasket(basket); - } + } async Task GetUserTokenAsync() { diff --git a/src/Web/WebMVC/Services/CampaignService.cs b/src/Web/WebMVC/Services/CampaignService.cs index 6190e74d6..7d61c9e7a 100644 --- a/src/Web/WebMVC/Services/CampaignService.cs +++ b/src/Web/WebMVC/Services/CampaignService.cs @@ -26,7 +26,7 @@ _apiClient = httpClient; _logger = logger; - _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/campaigns/"; + _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/m/campaigns/"; _httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor)); } diff --git a/src/Web/WebMVC/Services/CatalogService.cs b/src/Web/WebMVC/Services/CatalogService.cs index 2af428e2e..5b6fbe26b 100644 --- a/src/Web/WebMVC/Services/CatalogService.cs +++ b/src/Web/WebMVC/Services/CatalogService.cs @@ -25,7 +25,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services _apiClient = httpClient; _logger = logger; - _remoteServiceBaseUrl = $"{_settings.Value.CatalogUrl}/api/v1/catalog/"; + _remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1/c/catalog/"; } public async Task GetCatalogItems(int page, int take, int? brand, int? type) diff --git a/src/Web/WebMVC/Services/IBasketService.cs b/src/Web/WebMVC/Services/IBasketService.cs index 13921909a..cfbea5ff0 100644 --- a/src/Web/WebMVC/Services/IBasketService.cs +++ b/src/Web/WebMVC/Services/IBasketService.cs @@ -10,10 +10,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public interface IBasketService { Task GetBasket(ApplicationUser user); - Task AddItemToBasket(ApplicationUser user, BasketItem product); + Task AddItemToBasket(ApplicationUser user, int productId); Task UpdateBasket(Basket basket); Task Checkout(BasketDTO basket); Task SetQuantities(ApplicationUser user, Dictionary quantities); - Order MapBasketToOrder(Basket basket); + Task GetOrderDraft(string basketId); } } diff --git a/src/Web/WebMVC/Services/LocationService.cs b/src/Web/WebMVC/Services/LocationService.cs index 652484f4b..8bbdf743a 100644 --- a/src/Web/WebMVC/Services/LocationService.cs +++ b/src/Web/WebMVC/Services/LocationService.cs @@ -27,7 +27,7 @@ namespace WebMVC.Services _apiClient = httpClient; _logger = logger; - _remoteServiceBaseUrl = $"{_settings.Value.LocationsUrl}/api/v1/locations/"; + _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/l/locations/"; _httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor)); } diff --git a/src/Web/WebMVC/Services/OrderingService.cs b/src/Web/WebMVC/Services/OrderingService.cs index f36f1410d..ec9d2e8fd 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -21,7 +21,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public OrderingService(IOptionsSnapshot settings, IHttpContextAccessor httpContextAccesor, IHttpClient httpClient) { - _remoteServiceBaseUrl = $"{settings.Value.OrderingUrl}/api/v1/orders"; + _remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/api/v1/o/orders"; _settings = settings; _httpContextAccesor = httpContextAccesor; _apiClient = httpClient; diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 105b1ed9e..4bc2d8b88 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -130,6 +130,7 @@ namespace Microsoft.eShopOnContainers.WebMVC options.Scope.Add("basket"); options.Scope.Add("marketing"); options.Scope.Add("locations"); + options.Scope.Add("webshoppingagg"); }); } diff --git a/src/Web/WebMVC/ViewModels/CatalogItem.cs b/src/Web/WebMVC/ViewModels/CatalogItem.cs index 6dd216d1d..c869b7382 100644 --- a/src/Web/WebMVC/ViewModels/CatalogItem.cs +++ b/src/Web/WebMVC/ViewModels/CatalogItem.cs @@ -4,7 +4,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels { public class CatalogItem { - public string Id { get; set; } + public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } diff --git a/src/Web/WebMVC/Views/Catalog/_product.cshtml b/src/Web/WebMVC/Views/Catalog/_product.cshtml index 11138b55d..0fb1c39c4 100644 --- a/src/Web/WebMVC/Views/Catalog/_product.cshtml +++ b/src/Web/WebMVC/Views/Catalog/_product.cshtml @@ -12,13 +12,5 @@

@Model.Price.ToString("N2")
- - - - - - - - diff --git a/src/Web/WebSPA/AppSettings.cs b/src/Web/WebSPA/AppSettings.cs index fd254c3ae..c3fbd3b80 100644 --- a/src/Web/WebSPA/AppSettings.cs +++ b/src/Web/WebSPA/AppSettings.cs @@ -7,13 +7,13 @@ namespace eShopOnContainers.WebSPA { public class AppSettings { - public string BaseUrl { get; set; } - public string CatalogUrl { get; set; } - public string OrderingUrl { get; set; } public string IdentityUrl { get; set; } public string BasketUrl { get; set; } public string MarketingUrl { get; set; } + + public string PurchaseUrl { get; set; } + public string ActivateCampaignDetailFunction { get; set; } - public bool UseCustomizationData { get; set; } + public bool UseCustomizationData { get; set; } } } diff --git a/src/Web/WebSPA/Client/modules/basket/basket.service.ts b/src/Web/WebSPA/Client/modules/basket/basket.service.ts index 864915541..fef74974e 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.service.ts +++ b/src/Web/WebSPA/Client/modules/basket/basket.service.ts @@ -23,6 +23,7 @@ import { Subject } from 'rxjs/Subject'; @Injectable() export class BasketService { private basketUrl: string = ''; + private purchaseUrl: string = ''; basket: IBasket = { buyerId: '', items: [] @@ -40,12 +41,14 @@ export class BasketService { if (this.authService.UserData) { this.basket.buyerId = this.authService.UserData.sub; if (this.configurationService.isReady) { - this.basketUrl = this.configurationService.serverSettings.basketUrl; + this.basketUrl = this.configurationService.serverSettings.purchaseUrl; + this.purchaseUrl = this.configurationService.serverSettings.purchaseUrl; this.loadData(); } else { this.configurationService.settingsLoaded$.subscribe(x => { - this.basketUrl = this.configurationService.serverSettings.basketUrl; + this.basketUrl = this.configurationService.serverSettings.purchaseUrl; + this.purchaseUrl = this.configurationService.serverSettings.purchaseUrl; this.loadData(); }); } @@ -63,7 +66,7 @@ export class BasketService { } setBasket(basket): Observable { - let url = this.basketUrl + '/api/v1/basket/'; + let url = this.purchaseUrl + '/api/v1/basket/'; this.basket = basket; return this.service.post(url, basket).map((response: Response) => { return true; @@ -71,7 +74,7 @@ export class BasketService { } setBasketCheckout(basketCheckout): Observable { - let url = this.basketUrl + '/api/v1/basket/checkout'; + let url = this.basketUrl + '/api/v1/b/basket/checkout'; return this.service.postWithId(url, basketCheckout).map((response: Response) => { this.basketEvents.orderCreated(); return true; @@ -79,7 +82,7 @@ export class BasketService { } getBasket(): Observable { - let url = this.basketUrl + '/api/v1/basket/' + this.basket.buyerId; + let url = this.basketUrl + '/api/v1/b/basket/' + this.basket.buyerId; return this.service.get(url).map((response: Response) => { if (response.status === 204) { return null; diff --git a/src/Web/WebSPA/Client/modules/campaigns/campaigns.service.ts b/src/Web/WebSPA/Client/modules/campaigns/campaigns.service.ts index bd1678f44..2a8973322 100644 --- a/src/Web/WebSPA/Client/modules/campaigns/campaigns.service.ts +++ b/src/Web/WebSPA/Client/modules/campaigns/campaigns.service.ts @@ -33,7 +33,7 @@ export class CampaignsService { } getCampaigns(pageIndex: number, pageSize: number): Observable { - let url = this.marketingUrl + '/api/v1/campaigns/user'; + let url = this.marketingUrl + '/api/v1/m/campaigns/user'; url = url + '?pageIndex=' + pageIndex + '&pageSize=' + pageSize; return this.service.get(url).map((response: Response) => { @@ -42,7 +42,7 @@ export class CampaignsService { } getCampaign(id: number): Observable { - let url = this.marketingUrl + '/api/v1/campaigns/' + id; + let url = this.marketingUrl + '/api/v1/m/campaigns/' + id; return this.service.get(url).map((response: Response) => { return response.json(); diff --git a/src/Web/WebSPA/Client/modules/catalog/catalog.service.ts b/src/Web/WebSPA/Client/modules/catalog/catalog.service.ts index bdcb9d664..fc5bc4c5e 100644 --- a/src/Web/WebSPA/Client/modules/catalog/catalog.service.ts +++ b/src/Web/WebSPA/Client/modules/catalog/catalog.service.ts @@ -21,9 +21,9 @@ export class CatalogService { constructor(private service: DataService, private configurationService: ConfigurationService) { this.configurationService.settingsLoaded$.subscribe(x => { - this.catalogUrl = this.configurationService.serverSettings.catalogUrl + '/api/v1/catalog/items'; - this.brandUrl = this.configurationService.serverSettings.catalogUrl + '/api/v1/catalog/catalogbrands'; - this.typesUrl = this.configurationService.serverSettings.catalogUrl + '/api/v1/catalog/catalogtypes'; + this.catalogUrl = this.configurationService.serverSettings.purchaseUrl + '/api/v1/c/catalog/items'; + this.brandUrl = this.configurationService.serverSettings.purchaseUrl + '/api/v1/c/catalog/catalogbrands'; + this.typesUrl = this.configurationService.serverSettings.purchaseUrl + '/api/v1/c/catalog/catalogtypes'; }); } diff --git a/src/Web/WebSPA/Client/modules/orders/orders.service.ts b/src/Web/WebSPA/Client/modules/orders/orders.service.ts index 5eda7c8ce..5c563f836 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders.service.ts +++ b/src/Web/WebSPA/Client/modules/orders/orders.service.ts @@ -22,14 +22,14 @@ export class OrdersService { constructor(private service: DataService, private basketService: BasketWrapperService, private identityService: SecurityService, private configurationService: ConfigurationService) { if (this.configurationService.isReady) - this.ordersUrl = this.configurationService.serverSettings.orderingUrl; + this.ordersUrl = this.configurationService.serverSettings.purchaseUrl; else - this.configurationService.settingsLoaded$.subscribe(x => this.ordersUrl = this.configurationService.serverSettings.orderingUrl); + this.configurationService.settingsLoaded$.subscribe(x => this.ordersUrl = this.configurationService.serverSettings.purchaseUrl); } getOrders(): Observable { - let url = this.ordersUrl + '/api/v1/orders'; + let url = this.ordersUrl + '/api/v1/o/orders'; return this.service.get(url).map((response: Response) => { return response.json(); @@ -37,7 +37,7 @@ export class OrdersService { } getOrder(id: number): Observable { - let url = this.ordersUrl + '/api/v1/orders/' + id; + let url = this.ordersUrl + '/api/v1/o/orders/' + id; return this.service.get(url).map((response: Response) => { return response.json(); diff --git a/src/Web/WebSPA/Client/modules/shared/models/configuration.model.ts b/src/Web/WebSPA/Client/modules/shared/models/configuration.model.ts index f22b28a3e..49950e9d6 100644 --- a/src/Web/WebSPA/Client/modules/shared/models/configuration.model.ts +++ b/src/Web/WebSPA/Client/modules/shared/models/configuration.model.ts @@ -1,8 +1,6 @@ export interface IConfiguration { - catalogUrl: string, - orderingUrl: string, identityUrl: string, - basketUrl: string, marketingUrl: string, + purchaseUrl: string, activateCampaignDetailFunction: boolean } \ No newline at end of file diff --git a/src/Web/WebSPA/Client/modules/shared/services/configuration.service.ts b/src/Web/WebSPA/Client/modules/shared/services/configuration.service.ts index d41786859..ea95275e0 100644 --- a/src/Web/WebSPA/Client/modules/shared/services/configuration.service.ts +++ b/src/Web/WebSPA/Client/modules/shared/services/configuration.service.ts @@ -28,11 +28,9 @@ export class ConfigurationService { console.log('server settings loaded'); this.serverSettings = response.json(); console.log(this.serverSettings); - this.storageService.store('basketUrl', this.serverSettings.basketUrl); - this.storageService.store('catalogUrl', this.serverSettings.catalogUrl); this.storageService.store('identityUrl', this.serverSettings.identityUrl); - this.storageService.store('orderingUrl', this.serverSettings.orderingUrl); this.storageService.store('marketingUrl', this.serverSettings.marketingUrl); + this.storageService.store('purchaseUrl', this.serverSettings.purchaseUrl); this.storageService.store('activateCampaignDetailFunction', this.serverSettings.activateCampaignDetailFunction); this.isReady = true; this.settingsLoadedSource.next(); diff --git a/src/Web/WebSPA/Client/modules/shared/services/security.service.ts b/src/Web/WebSPA/Client/modules/shared/services/security.service.ts index 483ea4ae4..45007b5d1 100644 --- a/src/Web/WebSPA/Client/modules/shared/services/security.service.ts +++ b/src/Web/WebSPA/Client/modules/shared/services/security.service.ts @@ -82,7 +82,7 @@ export class SecurityService { let client_id = 'js'; let redirect_uri = location.origin + '/'; let response_type = 'id_token token'; - let scope = 'openid profile orders basket marketing locations'; + let scope = 'openid profile orders basket marketing locations webshoppingagg'; let nonce = 'N' + Math.random() + '' + Date.now(); let state = Date.now() + '' + Math.random(); diff --git a/src/Web/WebSPA/Properties/launchSettings.json b/src/Web/WebSPA/Properties/launchSettings.json index fd33f59ec..eff752f63 100644 --- a/src/Web/WebSPA/Properties/launchSettings.json +++ b/src/Web/WebSPA/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:5104/", + "applicationUrl": "http://localhost:58018/", "sslPort": 0 } }, diff --git a/src/Web/WebSPA/appsettings.json b/src/Web/WebSPA/appsettings.json index 7b1930f84..75f17ac35 100644 --- a/src/Web/WebSPA/appsettings.json +++ b/src/Web/WebSPA/appsettings.json @@ -1,10 +1,8 @@ { - "CatalogUrl": "http://localhost:5101", - "OrderingUrl": "http://localhost:5102", - "BasketUrl": "http://localhost:5103", "IdentityUrl": "http://localhost:5105", "MarketingUrl": "http://localhost:5110", "CallBackUrl": "http://localhost:5104/", + "PurchaseUrl": "http://localhost:5200", "UseCustomizationData": true, "IsClusterEnv": "False", "ActivateCampaignDetailFunction": true, diff --git a/src/Web/WebSPA/package-lock.json b/src/Web/WebSPA/package-lock.json index 313a88257..550b77da7 100644 --- a/src/Web/WebSPA/package-lock.json +++ b/src/Web/WebSPA/package-lock.json @@ -1176,6 +1176,7 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", + "fsevents": "1.1.3", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -2806,6 +2807,910 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.6.2", + "node-pre-gyp": "0.6.39" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true, + "dev": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + } + } + }, "fstream": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", diff --git a/test/Services/UnitTest/Basket/Application/CartControllerTest.cs b/test/Services/UnitTest/Basket/Application/CartControllerTest.cs index 6bdd2c43c..63f74cf35 100644 --- a/test/Services/UnitTest/Basket/Application/CartControllerTest.cs +++ b/test/Services/UnitTest/Basket/Application/CartControllerTest.cs @@ -92,7 +92,7 @@ namespace UnitTest.Basket.Application //Arrange var fakeCatalogItem = GetFakeCatalogItem(); - _basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny(), It.IsAny())) + _basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(1)); //Act @@ -118,7 +118,7 @@ namespace UnitTest.Basket.Application { return new CatalogItem() { - Id = "1", + Id = 1, Name = "fakeName", CatalogBrand = "fakeBrand", CatalogType = "fakeType", diff --git a/test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs b/test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs index 58d32c212..7410551e4 100644 --- a/test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs +++ b/test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs @@ -67,19 +67,19 @@ namespace UnitTest.Catalog.Application { new CatalogItem() { - Id = "1", + Id = 1, Name = "fakeItemA", CatalogTypeId = 1 }, new CatalogItem() { - Id = "2", + Id = 2, Name = "fakeItemB", CatalogTypeId = 1 }, new CatalogItem() { - Id = "3", + Id = 3, Name = "fakeItemC", CatalogTypeId = 1 } diff --git a/test/Services/UnitTest/Ordering/Application/OrderControllerTest.cs b/test/Services/UnitTest/Ordering/Application/OrderControllerTest.cs index 12f2be395..a60ce3bb3 100644 --- a/test/Services/UnitTest/Ordering/Application/OrderControllerTest.cs +++ b/test/Services/UnitTest/Ordering/Application/OrderControllerTest.cs @@ -68,40 +68,7 @@ namespace UnitTest.Ordering.Application Assert.IsAssignableFrom(viewResult.ViewData.Model); } - [Fact] - public async Task Get_create_order_success() - { - //Arrange - var fakeBuyerId = "1"; - var fakeBasket = GetFakeBasket(fakeBuyerId); - var fakeOrder = GetFakeOrder(); - - _basketServiceMock.Setup(x => x.GetBasket(It.IsAny())) - .Returns(Task.FromResult(fakeBasket)); - - _basketServiceMock.Setup(x => x.MapBasketToOrder(It.IsAny())) - .Returns(fakeOrder); - - _orderServiceMock.Setup(x => x.MapUserInfoIntoOrder(It.IsAny(), It.IsAny())) - .Returns(fakeOrder); - - //Act - var orderController = new OrderController(_orderServiceMock.Object, _basketServiceMock.Object, _identityParserMock.Object); - orderController.ControllerContext.HttpContext = _contextMock.Object; - var actionResult = await orderController.Create(); - - //Assert - var viewResult = Assert.IsType(actionResult); - Assert.IsAssignableFrom(viewResult.ViewData.Model); - } - - private BasketModel GetFakeBasket(string buyerId) - { - return new BasketModel() - { - BuyerId = buyerId - }; - } + private Order GetFakeOrder() { diff --git a/xglobal.json b/xglobal.json new file mode 100644 index 000000000..2ab18dceb --- /dev/null +++ b/xglobal.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version":"2.1.2" + } +} \ No newline at end of file