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 e3545c584..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-5110" -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-5110" -LocalAddress Any -LocalPort 5100-5110 -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 1348a6e9b..94bbfef83 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -92,6 +92,10 @@ services: ports: - "5433:1433" + nosql.data: + ports: + - "27017:27017" + webstatus: environment: - ASPNETCORE_ENVIRONMENT=Development @@ -103,4 +107,15 @@ services: - mvc=http://webmvc/hc - spa=http://webspa/hc ports: - - "5107:80" \ No newline at end of file + - "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.vs.debug.yml b/docker-compose.vs.debug.yml index 85195a493..c2e5e4a06 100644 --- a/docker-compose.vs.debug.yml +++ b/docker-compose.vs.debug.yml @@ -122,3 +122,17 @@ services: 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 f01b21eff..87e246780 100644 --- a/docker-compose.vs.release.yml +++ b/docker-compose.vs.release.yml @@ -80,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 9612b38fb..e9fb14179 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,6 +68,9 @@ services: sql.data: image: microsoft/mssql-server-linux + nosql.data: + image: mongo + basket.data: image: redis ports: @@ -83,4 +86,11 @@ services: build: context: ./src/Web/WebStatus dockerfile: Dockerfile - + + 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 a874f5a7e..22b122fe5 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -1200,5 +1200,9 @@ Global {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/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">