diff --git a/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 b/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 index be63d8a25..865f24067 100644 --- a/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 +++ b/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 @@ -21,6 +21,6 @@ try { Write-Host "Rule found" } catch [Exception] { - New-NetFirewallRule -DisplayName eShopOnContainers-Inbound -Confirm -Description "eShopOnContainers Inbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5105 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Inbound - New-NetFirewallRule -DisplayName eShopOnContainers-Outbound -Confirm -Description "eShopOnContainers Outbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5105 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Outbound + New-NetFirewallRule -DisplayName eShopOnContainers-Inbound -Confirm -Description "eShopOnContainers Inbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5110 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Inbound + New-NetFirewallRule -DisplayName eShopOnContainers-Outbound -Confirm -Description "eShopOnContainers Outbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5110 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Outbound } \ No newline at end of file diff --git a/docker-compose-windows.override.yml b/docker-compose-windows.override.yml index c3733c164..45b2db748 100644 --- a/docker-compose-windows.override.yml +++ b/docker-compose-windows.override.yml @@ -80,4 +80,19 @@ services: - SA_PASSWORD=Pass@word - ACCEPT_EULA=Y ports: - - "5433:1433" \ No newline at end of file + - "5433:1433" + + nosql.data: + ports: + - "27017:27017" + + locations.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=mongodb://nosql.data + - Database=LocationsDb + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - EventBusConnection=rabbitmq + ports: + - "5109:80" \ No newline at end of file diff --git a/docker-compose-windows.yml b/docker-compose-windows.yml index 48f7ef7b4..2caf209ab 100644 --- a/docker-compose-windows.yml +++ b/docker-compose-windows.yml @@ -53,9 +53,20 @@ services: - ordering.api - identity.api - basket.api - + + locations.api: + image: locations.api + build: + context: ./src/Services/Location/Locations.API + dockerfile: Dockerfile + depends_on: + - nosql.data + sql.data: image: microsoft/mssql-server-windows + + nosql.data: + image: mongo:windowsservercore basket.data: image: redis:nanoserver diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 04d1c4d9c..94bbfef83 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -49,6 +49,15 @@ services: ports: - "5102:80" + marketing.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + ports: + - "5110:80" + webspa: environment: - ASPNETCORE_ENVIRONMENT=Development @@ -83,6 +92,10 @@ services: ports: - "5433:1433" + nosql.data: + ports: + - "27017:27017" + webstatus: environment: - ASPNETCORE_ENVIRONMENT=Development @@ -95,3 +108,14 @@ services: - spa=http://webspa/hc ports: - "5107:80" + + locations.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=mongodb://nosql.data + - Database=LocationsDb + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + - EventBusConnection=rabbitmq + ports: + - "5109:80" \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index c5d8839ea..b9c46b4c2 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -54,6 +54,15 @@ services: ports: - "5102:80" + marketing.api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + ports: + - "5110:80" + webspa: environment: - ASPNETCORE_ENVIRONMENT=Production diff --git a/docker-compose.vs.debug.yml b/docker-compose.vs.debug.yml index 2e7145637..c2e5e4a06 100644 --- a/docker-compose.vs.debug.yml +++ b/docker-compose.vs.debug.yml @@ -61,6 +61,22 @@ services: labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" + marketing.api: + image: eshop/marketing.api:dev + build: + args: + source: ${DOCKER_BUILD_SOURCE} + environment: + - DOTNET_USE_POLLING_FILE_WATCHER=1 + volumes: + - ./src/Services/Marketing/Marketing.API:/app + - ~/.nuget/packages:/root/.nuget/packages:ro + - ~/clrdbg:/clrdbg:ro + entrypoint: tail -f /dev/null + labels: + - "com.microsoft.visualstudio.targetoperatingsystem=linux" + + webspa: image: eshop/webspa:dev build: @@ -105,3 +121,18 @@ services: entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" + + locations.api: + image: eshop/locations.api:dev + build: + args: + source: ${DOCKER_BUILD_SOURCE} + environment: + - DOTNET_USE_POLLING_FILE_WATCHER=1 + volumes: + - ./src/Services/Location/Locations.API:/app + - ~/.nuget/packages:/root/.nuget/packages:ro + - ~/clrdbg:/clrdbg:ro + entrypoint: tail -f /dev/null + labels: + - "com.microsoft.visualstudio.targetoperatingsystem=linux" diff --git a/docker-compose.vs.release.yml b/docker-compose.vs.release.yml index d1ca5b2c6..87e246780 100644 --- a/docker-compose.vs.release.yml +++ b/docker-compose.vs.release.yml @@ -41,6 +41,16 @@ services: labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" + marketing.api: + build: + args: + source: ${DOCKER_BUILD_SOURCE} + volumes: + - ~/clrdbg:/clrdbg:ro + entrypoint: tail -f /dev/null + labels: + - "com.microsoft.visualstudio.targetoperatingsystem=linux" + webspa: build: args: @@ -70,3 +80,13 @@ services: entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" + + locations.api: + build: + args: + source: ${DOCKER_BUILD_SOURCE} + volumes: + - ~/clrdbg:/clrdbg:ro + entrypoint: tail -f /dev/null + labels: + - "com.microsoft.visualstudio.targetoperatingsystem=linux" diff --git a/docker-compose.yml b/docker-compose.yml index 21f3972f2..e9fb14179 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,15 @@ services: depends_on: - sql.data + marketing.api: + image: eshop/marketing.api + build: + context: ./src/Services/Marketing/Marketing.API + dockerfile: Dockerfile + depends_on: + - sql.data + - identity.api + webspa: image: eshop/webspa build: @@ -59,6 +68,9 @@ services: sql.data: image: microsoft/mssql-server-linux + nosql.data: + image: mongo + basket.data: image: redis ports: @@ -74,4 +86,11 @@ services: build: context: ./src/Web/WebStatus dockerfile: Dockerfile - \ No newline at end of file + + locations.api: + image: locations.api + build: + context: ./src/Services/Location/Locations.API + dockerfile: Dockerfile + depends_on: + - nosql.data \ No newline at end of file diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index d9c994954..22b122fe5 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -76,6 +76,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Health EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Location", "Location", "{41139F64-4046-4F16-96B7-D941D96FA9C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Locations.API", "src\Services\Location\Locations.API\Locations.API.csproj", "{E7581357-FC34-474C-B8F5-307EE3CE05EF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataProtection", "DataProtection", "{88B22DBB-AA8F-4290-A454-2C109352C345}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataProtection", "src\BuildingBlocks\DataProtection\DataProtection\DataProtection.csproj", "{23A33F9B-7672-426D-ACF9-FF8436ADC81A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Marketing", "Marketing", "{A5260DE0-1FDD-467E-9CC1-A028AB081CEE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.API", "src\Services\Marketing\Marketing.API\Marketing.API.csproj", "{DF395F85-B010-465D-857A-7EBCC512C0C2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1002,6 +1014,150 @@ Global {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.Build.0 = Release|Any CPU {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.ActiveCfg = Release|Any CPU {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.Build.0 = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|ARM.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhone.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x64.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x64.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x86.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.AppStore|x86.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|ARM.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhone.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x64.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x64.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x86.ActiveCfg = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Debug|x86.Build.0 = Debug|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|Any CPU.Build.0 = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|ARM.ActiveCfg = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|ARM.Build.0 = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhone.ActiveCfg = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhone.Build.0 = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x64.ActiveCfg = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x64.Build.0 = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x86.ActiveCfg = Release|Any CPU + {E7581357-FC34-474C-B8F5-307EE3CE05EF}.Release|x86.Build.0 = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|ARM.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhone.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x64.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.AppStore|x86.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|ARM.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhone.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x64.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.ActiveCfg = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Debug|x86.Build.0 = Debug|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|Any CPU.Build.0 = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.ActiveCfg = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|ARM.Build.0 = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.ActiveCfg = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhone.Build.0 = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.ActiveCfg = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x64.Build.0 = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.ActiveCfg = Release|Any CPU + {23A33F9B-7672-426D-ACF9-FF8436ADC81A}.Release|x86.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|ARM.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhone.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x64.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x64.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x86.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x86.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|ARM.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhone.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x64.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x86.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|Any CPU.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|ARM.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|ARM.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhone.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhone.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x64.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x64.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x86.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1038,5 +1194,15 @@ Global {22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379} {4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379} {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} + {41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} + {E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6} + {88B22DBB-AA8F-4290-A454-2C109352C345} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} + {23A33F9B-7672-426D-ACF9-FF8436ADC81A} = {88B22DBB-AA8F-4290-A454-2C109352C345} + {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} + {DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} + {88B22DBB-AA8F-4290-A454-2C109352C345} = {DB0EFB20-B024-4E5E-A75C-52143C131D25} + {23A33F9B-7672-426D-ACF9-FF8436ADC81A} = {88B22DBB-AA8F-4290-A454-2C109352C345} + {41139F64-4046-4F16-96B7-D941D96FA9C6} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} + {E7581357-FC34-474C-B8F5-307EE3CE05EF} = {41139F64-4046-4F16-96B7-D941D96FA9C6} EndGlobalSection EndGlobal diff --git a/k8s/deploy.ps1 b/k8s/deploy.ps1 index 0619dd8e8..9930474ba 100644 --- a/k8s/deploy.ps1 +++ b/k8s/deploy.ps1 @@ -58,7 +58,7 @@ ExecKube -cmd 'delete configmap urls' # start sql, rabbitmq, frontend deploymentsExecKube -cmd 'delete configmap config-files' ExecKube -cmd 'create configmap config-files --from-file=nginx-conf=nginx.conf' ExecKube -cmd 'label configmap config-files app=eshop' -ExecKube -cmd 'create -f sql-data.yaml -f basket-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml' +ExecKube -cmd 'create -f sql-data.yaml -f basket-data.yaml -f keystore-data.yaml -f rabbitmq.yaml -f services.yaml -f frontend.yaml' # building and publishing docker images not necessary when deploying through CI VSTS if(-not $deployCI) { diff --git a/k8s/deployments.yaml b/k8s/deployments.yaml index 1bb406390..62352e2ef 100644 --- a/k8s/deployments.yaml +++ b/k8s/deployments.yaml @@ -85,6 +85,10 @@ spec: value: http://0.0.0.0:80/identity - name: ConnectionStrings__DefaultConnection value: "Server=sql-data;Initial Catalog=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word" + - name: DPConnectionString + value: keystore-data + - name: IsClusterEnv + value: 'True' - name: MvcClient valueFrom: configMapKeyRef: @@ -152,6 +156,10 @@ spec: env: - name: ASPNETCORE_URLS value: http://0.0.0.0:80/webmvc + - name: DPConnectionString + value: keystore-data + - name: IsClusterEnv + value: 'True' - name: BasketUrl valueFrom: configMapKeyRef: @@ -255,6 +263,10 @@ spec: env: - name: ASPNETCORE_URLS value: http://0.0.0.0:80 + - name: DPConnectionString + value: keystore-data + - name: IsClusterEnv + value: 'True' - name: BasketUrl valueFrom: configMapKeyRef: diff --git a/k8s/keystore-data.yaml b/k8s/keystore-data.yaml new file mode 100644 index 000000000..3340cce35 --- /dev/null +++ b/k8s/keystore-data.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: eshop + component: keystore-data + name: keystore-data +spec: + ports: + - port: 6379 + selector: + app: eshop + component: keystore-data +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: keystore-data +spec: + template: + metadata: + labels: + app: eshop + component: keystore-data + spec: + containers: + - name: keystore-data + image: redis:3.2-alpine + diff --git a/src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj b/src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj new file mode 100644 index 000000000..bfe61a85a --- /dev/null +++ b/src/BuildingBlocks/DataProtection/DataProtection/DataProtection.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp1.1 + Microsoft.eShopOnContainers.BuildingBlocks + + + + + + + + \ No newline at end of file diff --git a/src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs b/src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs new file mode 100644 index 000000000..3db776b9a --- /dev/null +++ b/src/BuildingBlocks/DataProtection/DataProtection/DataProtectionBuilderExtensions.cs @@ -0,0 +1,95 @@ +namespace Microsoft.eShopOnContainers.BuildingBlocks +{ + using Microsoft.AspNetCore.DataProtection; + using Microsoft.AspNetCore.DataProtection.Repositories; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using System; + using System.Linq; + using System.Net; + + /// + /// Extension methods for for configuring + /// data protection options. + /// + public static class DataProtectionBuilderExtensions + { + /// + /// Sets up data protection to persist session keys in Redis. + /// + /// The used to set up data protection options. + /// The connection string specifying the Redis instance and database for key storage. + /// + /// The for continued configuration. + /// + /// + /// Thrown if or is . + /// + /// + /// Thrown if is empty. + /// + public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, string redisConnectionString) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (redisConnectionString == null) + { + throw new ArgumentNullException(nameof(redisConnectionString)); + } + + if (redisConnectionString.Length == 0) + { + throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString)); + } + + var ips = Dns.GetHostAddressesAsync(redisConnectionString).Result; + + return builder.Use(ServiceDescriptor.Singleton(services => + new RedisXmlRepository(ips.First().ToString(), services.GetRequiredService>()))); + } + + /// + /// Updates an to use the service of + /// a specific type, removing all other services of that type. + /// + /// The that should use the specified service. + /// The with the service the should use. + /// + /// The for continued configuration. + /// + /// + /// Thrown if or is . + /// + public static IDataProtectionBuilder Use(this IDataProtectionBuilder builder, ServiceDescriptor descriptor) + { + // This algorithm of removing all other services of a specific type + // before adding the new/replacement service is how the base ASP.NET + // DataProtection bits work. Due to some of the differences in how + // that base set of bits handles DI, it's better to follow suit + // and work in the same way than to try and debug weird issues. + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (descriptor == null) + { + throw new ArgumentNullException(nameof(descriptor)); + } + + for (int i = builder.Services.Count - 1; i >= 0; i--) + { + if (builder.Services[i]?.ServiceType == descriptor.ServiceType) + { + builder.Services.RemoveAt(i); + } + } + + builder.Services.Add(descriptor); + return builder; + } + } +} diff --git a/src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs b/src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs new file mode 100644 index 000000000..f5a903b65 --- /dev/null +++ b/src/BuildingBlocks/DataProtection/DataProtection/RedisXmlRepository.cs @@ -0,0 +1,210 @@ +namespace Microsoft.eShopOnContainers.BuildingBlocks +{ + using Microsoft.AspNetCore.DataProtection.Repositories; + using Microsoft.Extensions.Logging; + using StackExchange.Redis; + using System; + using System.Collections.Generic; + using System.Text.RegularExpressions; + using System.Xml.Linq; + + /// + /// Key repository that stores XML encrypted keys in a Redis distributed cache. + /// + /// + /// + /// The values stored in Redis are XML documents that contain encrypted session + /// keys used for the protection of things like session state. The document contents + /// are double-encrypted - first with a changing session key; then by a master key. + /// As such, there's no risk in storing the keys in Redis - even if someone can crack + /// the master key, they still need to also crack the session key. (Other solutions + /// for sharing keys across a farm environment include writing them to files + /// on a file share.) + /// + /// + /// While the repository uses a hash to keep the set of encrypted keys separate, you + /// can further separate these items from other items in Redis by specifying a unique + /// database in the connection string. + /// + /// + /// Consumers of the repository are responsible for caching the XML items as needed. + /// Typically repositories are consumed by things like + /// which generates + /// values that get cached. The mechanism is already optimized for caching so there's + /// no need to create a redundant cache. + /// + /// + /// + /// + public class RedisXmlRepository : IXmlRepository, IDisposable + { + /// + /// The root cache key for XML items stored in Redis + /// + public static readonly string RedisHashKey = "DataProtectionXmlRepository"; + + /// + /// The connection to the Redis backing store. + /// + private IConnectionMultiplexer _connection; + + /// + /// Flag indicating whether the object has been disposed. + /// + private bool _disposed = false; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The Redis connection string. + /// + /// + /// The used to log diagnostic messages. + /// + /// + /// Thrown if or is . + /// + public RedisXmlRepository(string connectionString, ILogger logger) + : this(ConnectionMultiplexer.Connect(connectionString), logger) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The Redis database connection. + /// + /// + /// The used to log diagnostic messages. + /// + /// + /// Thrown if or is . + /// + public RedisXmlRepository(IConnectionMultiplexer connection, ILogger logger) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + this._connection = connection; + this.Logger = logger; + + // Mask the password so it doesn't get logged. + var configuration = Regex.Replace(this._connection.Configuration, @"password\s*=\s*[^,]*", "password=****", RegexOptions.IgnoreCase); + this.Logger.LogDebug("Storing data protection keys in Redis: {RedisConfiguration}", configuration); + } + + /// + /// Gets the logger. + /// + /// + /// The used to log diagnostic messages. + /// + public ILogger Logger { get; private set; } + + /// + /// Performs application-defined tasks associated with freeing, releasing, + /// or resetting unmanaged resources. + /// + public void Dispose() + { + this.Dispose(true); + } + + /// + /// Gets all top-level XML elements in the repository. + /// + /// + /// An with the set of elements + /// stored in the repository. + /// + public IReadOnlyCollection GetAllElements() + { + var database = this._connection.GetDatabase(); + var hash = database.HashGetAll(RedisHashKey); + var elements = new List(); + + if (hash == null || hash.Length == 0) + { + return elements.AsReadOnly(); + } + + foreach (var item in hash.ToStringDictionary()) + { + elements.Add(XElement.Parse(item.Value)); + } + + this.Logger.LogDebug("Read {XmlElementCount} XML elements from Redis.", elements.Count); + return elements.AsReadOnly(); + } + + /// + /// Adds a top-level XML element to the repository. + /// + /// The element to add. + /// + /// An optional name to be associated with the XML element. + /// For instance, if this repository stores XML files on disk, the friendly name may + /// be used as part of the file name. Repository implementations are not required to + /// observe this parameter even if it has been provided by the caller. + /// + /// + /// The parameter must be unique if specified. + /// For instance, it could be the ID of the key being stored. + /// + /// + /// Thrown if is . + /// + public void StoreElement(XElement element, string friendlyName) + { + if (element == null) + { + throw new ArgumentNullException(nameof(element)); + } + + if (string.IsNullOrEmpty(friendlyName)) + { + // The framework always passes in a name, but + // the contract indicates this may be null or empty. + friendlyName = Guid.NewGuid().ToString(); + } + + this.Logger.LogDebug("Storing XML element with friendly name {XmlElementFriendlyName}.", friendlyName); + + this._connection.GetDatabase().HashSet(RedisHashKey, friendlyName, element.ToString()); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (!this._disposed) + { + if (disposing) + { + if (this._connection != null) + { + this._connection.Close(); + this._connection.Dispose(); + } + } + + this._connection = null; + this._disposed = true; + } + } + } +} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs index a75ee7058..b62bb7ba3 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/GlobalSettings.cs @@ -43,6 +43,8 @@ public string IdentityEndpoint { get; set; } + public string LocationEndpoint { get; set; } + public string UserInfoEndpoint { get; set; } public string LogoutEndpoint { get; set; } @@ -62,6 +64,7 @@ LogoutEndpoint = string.Format("{0}:5105/connect/endsession", baseEndpoint); IdentityCallback = string.Format("{0}:5105/xamarincallback", baseEndpoint); LogoutCallback = string.Format("{0}:5105/Account/Redirecting", baseEndpoint); + LocationEndpoint = string.Format("{0}:5109", baseEndpoint); } } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs index 24da4fd2b..51b1efe30 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs @@ -24,9 +24,13 @@ namespace eShopOnContainers.Core.Helpers private const string IdToken = "id_token"; private const string IdUseMocks = "use_mocks"; private const string IdUrlBase = "url_base"; + private const string IdUseFakeLocation = "use_fake_location"; + private const string IdFakeLatitude = "fake_latitude"; + private const string IdFakeLongitude = "fake_longitude"; private static readonly string AccessTokenDefault = string.Empty; private static readonly string IdTokenDefault = string.Empty; private static readonly bool UseMocksDefault = true; + private static readonly bool UseFakeLocationDefault = false; private static readonly string UrlBaseDefault = GlobalSetting.Instance.BaseEndpoint; #endregion @@ -78,5 +82,40 @@ namespace eShopOnContainers.Core.Helpers AppSettings.AddOrUpdateValue(IdUrlBase, value); } } + + public static bool UseFakeLocation + { + get + { + return AppSettings.GetValueOrDefault(IdUseFakeLocation, UseFakeLocationDefault); + } + set + { + AppSettings.AddOrUpdateValue(IdUseFakeLocation, value); + } + } + + public static double FakeLatitude + { + get + { + return AppSettings.GetValueOrDefault(IdFakeLatitude); + } + set + { + AppSettings.AddOrUpdateValue(IdFakeLatitude, value); + } + } + public static double FakeLongitude + { + get + { + return AppSettings.GetValueOrDefault(IdFakeLongitude); + } + set + { + AppSettings.AddOrUpdateValue(IdFakeLongitude, value); + } + } } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Location/LocationRequest.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Location/LocationRequest.cs new file mode 100644 index 000000000..99654914c --- /dev/null +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Models/Location/LocationRequest.cs @@ -0,0 +1,8 @@ +namespace eShopOnContainers.Core.Models.Location +{ + public class LocationRequest + { + public double Longitude { get; set; } + public double Latitude { get; set; } + } +} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/ILocationService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/ILocationService.cs new file mode 100644 index 000000000..bc2c59915 --- /dev/null +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/ILocationService.cs @@ -0,0 +1,10 @@ +namespace eShopOnContainers.Core.Services.Location +{ + using System.Threading.Tasks; + using eShopOnContainers.Core.Models.Location; + + public interface ILocationService + { + Task UpdateUserLocation(LocationRequest newLocReq); + } +} \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs new file mode 100644 index 000000000..2149a5118 --- /dev/null +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Location/LocationService.cs @@ -0,0 +1,28 @@ +namespace eShopOnContainers.Core.Services.Location +{ + using eShopOnContainers.Core.Models.Location; + using eShopOnContainers.Core.Services.RequestProvider; + using System; + using System.Threading.Tasks; + + public class LocationService : ILocationService + { + private readonly IRequestProvider _requestProvider; + + public LocationService(IRequestProvider requestProvider) + { + _requestProvider = requestProvider; + } + + public async Task UpdateUserLocation(LocationRequest newLocReq) + { + UriBuilder builder = new UriBuilder(GlobalSetting.Instance.LocationEndpoint); + + builder.Path = "api/v1/locations"; + + string uri = builder.ToString(); + + var result = await _requestProvider.PostAsync(uri, newLocReq); + } + } +} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs index 90abb8f09..3eda91b42 100755 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs @@ -11,6 +11,7 @@ using eShopOnContainers.Core.Services.Identity; using eShopOnContainers.Core.Services.Order; using eShopOnContainers.Core.Services.User; using Xamarin.Forms; +using eShopOnContainers.Core.Services.Location; namespace eShopOnContainers.Core.ViewModels.Base { @@ -53,24 +54,25 @@ namespace eShopOnContainers.Core.ViewModels.Base builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); - if (useMockServices) + if (useMockServices) { builder.RegisterInstance(new CatalogMockService()).As(); builder.RegisterInstance(new BasketMockService()).As(); builder.RegisterInstance(new OrderMockService()).As(); builder.RegisterInstance(new UserMockService()).As(); - UseMockService = true; + UseMockService = true; } else { builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); - UseMockService = false; + UseMockService = false; } if (_container != null) diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs index 4596eb25c..8bfe9ed25 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs @@ -4,38 +4,51 @@ using Xamarin.Forms; using System.Threading.Tasks; using eShopOnContainers.Core.Helpers; using eShopOnContainers.Core.Models.User; +using System; +using eShopOnContainers.Core.Models.Location; +using eShopOnContainers.Core.Services.Location; namespace eShopOnContainers.Core.ViewModels { public class SettingsViewModel : ViewModelBase { - private string _title; - private string _description; + private string _titleUseAzureServices; + private string _descriptionUseAzureServices; private bool _useAzureServices; + private string _titleUseFakeLocation; + private string _descriptionUseFakeLocation; + private bool _useFakeLocation; private string _endpoint; + private double _latitude; + private double _longitude; + private ILocationService _locationService; - public SettingsViewModel() + public SettingsViewModel(ILocationService locationService) { UseAzureServices = !Settings.UseMocks; + _useFakeLocation = Settings.UseFakeLocation; + _latitude = Settings.FakeLatitude; + _longitude = Settings.FakeLongitude; + _locationService = locationService; } - public string Title + public string TitleUseAzureServices { - get { return _title; } + get { return _titleUseAzureServices; } set { - _title = value; - RaisePropertyChanged(() => Title); + _titleUseAzureServices = value; + RaisePropertyChanged(() => TitleUseAzureServices); } } - public string Description + public string DescriptionUseAzureServices { - get { return _description; } + get { return _descriptionUseAzureServices; } set { - _description = value; - RaisePropertyChanged(() => Description); + _descriptionUseAzureServices = value; + RaisePropertyChanged(() => DescriptionUseAzureServices); } } @@ -52,6 +65,39 @@ namespace eShopOnContainers.Core.ViewModels } } + public string TitleUseFakeLocation + { + get { return _titleUseFakeLocation; } + set + { + _titleUseFakeLocation = value; + RaisePropertyChanged(() => TitleUseFakeLocation); + } + } + + public string DescriptionUseFakeLocation + { + get { return _descriptionUseFakeLocation; } + set + { + _descriptionUseFakeLocation = value; + RaisePropertyChanged(() => DescriptionUseFakeLocation); + } + } + + public bool UseFakeLocation + { + get { return _useFakeLocation; } + set + { + _useFakeLocation = value; + + // Save use fake location services to local storage + Settings.UseFakeLocation = _useFakeLocation; + RaisePropertyChanged(() => UseFakeLocation); + } + } + public string Endpoint { get { return _endpoint; } @@ -68,12 +114,47 @@ namespace eShopOnContainers.Core.ViewModels } } + public double Latitude + { + get { return _latitude; } + set + { + _latitude = value; + + UpdateLatitude(_latitude); + + RaisePropertyChanged(() => Latitude); + } + } + + public double Longitude + { + get { return _longitude; } + set + { + _longitude = value; + + UpdateLongitude(_longitude); + + RaisePropertyChanged(() => Longitude); + } + } + public ICommand ToggleMockServicesCommand => new Command(async () => await ToggleMockServicesAsync()); + public ICommand ToggleFakeLocationCommand => new Command(() => ToggleFakeLocationAsync()); + + public ICommand ToggleSendLocationCommand => new Command(async () => await ToggleSendLocationAsync()); + public override Task InitializeAsync(object navigationData) { UpdateInfo(); + UpdateInfoFakeLocation(); + Endpoint = Settings.UrlBase; + _latitude = Settings.FakeLatitude; + _longitude = Settings.FakeLongitude; + _useFakeLocation = Settings.UseFakeLocation; return base.InitializeAsync(navigationData); } @@ -100,17 +181,48 @@ namespace eShopOnContainers.Core.ViewModels } } - private void UpdateInfo() + private void ToggleFakeLocationAsync() + { + ViewModelLocator.RegisterDependencies(!UseAzureServices); + UpdateInfoFakeLocation(); + } + + private async Task ToggleSendLocationAsync() + { + LocationRequest locationRequest = new LocationRequest + { + Latitude = _latitude, + Longitude = _longitude + }; + + await _locationService.UpdateUserLocation(locationRequest); + } + + private void UpdateInfo() { if (!UseAzureServices) { - Title = "Use Mock Services"; - Description = "Mock Services are simulated objects that mimic the behavior of real services using a controlled approach."; + TitleUseAzureServices = "Use Mock Services"; + DescriptionUseAzureServices = "Mock Services are simulated objects that mimic the behavior of real services using a controlled approach."; } else { - Title = "Use Microservices/Containers from eShopOnContainers"; - Description = "When enabling the use of microservices/containers, the app will attempt to use real services deployed as Docker containers at the specified base endpoint, which will must be reachable through the network."; + TitleUseAzureServices = "Use Microservices/Containers from eShopOnContainers"; + DescriptionUseAzureServices = "When enabling the use of microservices/containers, the app will attempt to use real services deployed as Docker containers at the specified base endpoint, which will must be reachable through the network."; + } + } + + private void UpdateInfoFakeLocation() + { + if (!UseFakeLocation) + { + TitleUseFakeLocation = "Use Fake Location"; + DescriptionUseFakeLocation = "Fake Location are added for marketing campaign testing."; + } + else + { + TitleUseFakeLocation = "Use Real Location"; + DescriptionUseFakeLocation = "When enabling the use of real location, the app will attempt to use real location from the device."; } } @@ -119,5 +231,17 @@ namespace eShopOnContainers.Core.ViewModels // Update remote endpoint (save to local storage) Settings.UrlBase = endpoint; } + + private void UpdateLatitude(double latitude) + { + // Update fake latitude (save to local storage) + Settings.FakeLatitude = latitude; + } + + private void UpdateLongitude(double longitude) + { + // Update fake longitude (save to local storage) + Settings.FakeLongitude = longitude; + } } } \ No newline at end of file diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml index 7db37dd66..a8da35daa 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml @@ -108,11 +108,11 @@ Grid.Column="0" Grid.Row="1">