Merge locations feature branch
This commit is contained in:
commit
0337932697
@ -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
|
||||
}
|
@ -80,4 +80,19 @@ services:
|
||||
- SA_PASSWORD=Pass@word
|
||||
- ACCEPT_EULA=Y
|
||||
ports:
|
||||
- "5433:1433"
|
||||
- "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"
|
@ -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
|
||||
|
@ -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"
|
||||
- "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"
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
@ -76,6 +76,10 @@ 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}"
|
||||
@ -1146,5 +1150,7 @@ Global
|
||||
{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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<string>(IdUrlBase, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool UseFakeLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
return AppSettings.GetValueOrDefault<bool>(IdUseFakeLocation, UseFakeLocationDefault);
|
||||
}
|
||||
set
|
||||
{
|
||||
AppSettings.AddOrUpdateValue<bool>(IdUseFakeLocation, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static double FakeLatitude
|
||||
{
|
||||
get
|
||||
{
|
||||
return AppSettings.GetValueOrDefault<double>(IdFakeLatitude);
|
||||
}
|
||||
set
|
||||
{
|
||||
AppSettings.AddOrUpdateValue<double>(IdFakeLatitude, value);
|
||||
}
|
||||
}
|
||||
public static double FakeLongitude
|
||||
{
|
||||
get
|
||||
{
|
||||
return AppSettings.GetValueOrDefault<double>(IdFakeLongitude);
|
||||
}
|
||||
set
|
||||
{
|
||||
AppSettings.AddOrUpdateValue<double>(IdFakeLongitude, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace eShopOnContainers.Core.Models.Location
|
||||
{
|
||||
public class LocationRequest
|
||||
{
|
||||
public double Longitude { get; set; }
|
||||
public double Latitude { get; set; }
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<OpenUrlService>().As<IOpenUrlService>();
|
||||
builder.RegisterType<IdentityService>().As<IIdentityService>();
|
||||
builder.RegisterType<RequestProvider>().As<IRequestProvider>();
|
||||
builder.RegisterType<LocationService>().As<ILocationService>().SingleInstance();
|
||||
|
||||
if (useMockServices)
|
||||
if (useMockServices)
|
||||
{
|
||||
builder.RegisterInstance(new CatalogMockService()).As<ICatalogService>();
|
||||
builder.RegisterInstance(new BasketMockService()).As<IBasketService>();
|
||||
builder.RegisterInstance(new OrderMockService()).As<IOrderService>();
|
||||
builder.RegisterInstance(new UserMockService()).As<IUserService>();
|
||||
|
||||
UseMockService = true;
|
||||
UseMockService = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.RegisterType<CatalogService>().As<ICatalogService>().SingleInstance();
|
||||
builder.RegisterType<BasketService>().As<IBasketService>().SingleInstance();
|
||||
builder.RegisterType<OrderService>().As<IOrderService>().SingleInstance();
|
||||
builder.RegisterType<UserService>().As<IUserService>().SingleInstance();
|
||||
builder.RegisterType<UserService>().As<IUserService>().SingleInstance();
|
||||
|
||||
UseMockService = false;
|
||||
UseMockService = false;
|
||||
}
|
||||
|
||||
if (_container != null)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -108,11 +108,11 @@
|
||||
Grid.Column="0"
|
||||
Grid.Row="1">
|
||||
<Label
|
||||
Text="{Binding Title}"
|
||||
Text="{Binding TitleUseAzureServices}"
|
||||
TextColor="{StaticResource GreenColor}"
|
||||
Style="{StaticResource SettingsTitleStyle}"/>
|
||||
<Label
|
||||
Text="{Binding Description}"
|
||||
Text="{Binding DescriptionUseAzureServices}"
|
||||
Style="{StaticResource SettingsDescriptionStyle}"/>
|
||||
</StackLayout>
|
||||
<!-- ON / OFF -->
|
||||
@ -160,8 +160,79 @@
|
||||
<Grid
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"/>
|
||||
<StackLayout
|
||||
Grid.Column="0"
|
||||
Grid.Row="4">
|
||||
<Label
|
||||
Text="{Binding TitleUseFakeLocation}"
|
||||
TextColor="{StaticResource GreenColor}"
|
||||
Style="{StaticResource SettingsTitleStyle}"/>
|
||||
<Label
|
||||
Text="{Binding DescriptionUseFakeLocation}"
|
||||
Style="{StaticResource SettingsDescriptionStyle}"/>
|
||||
</StackLayout>
|
||||
<!-- ON / OFF -->
|
||||
<controls:ToggleButton
|
||||
Grid.Column="1"
|
||||
Grid.Row="4"
|
||||
Animate="True"
|
||||
Checked="{Binding UseFakeLocation, Mode=TwoWay}"
|
||||
Command="{Binding ToggleFakeLocationCommand}"
|
||||
Style="{StaticResource SettingsToggleButtonStyle}">
|
||||
<controls:ToggleButton.CheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_on.png"
|
||||
iOS="switchOn.png"
|
||||
WinPhone="Assets/switchOn.png"/>
|
||||
</controls:ToggleButton.CheckedImage>
|
||||
<controls:ToggleButton.UnCheckedImage>
|
||||
<OnPlatform x:TypeArguments="ImageSource"
|
||||
Android="switch_off.png"
|
||||
iOS="switchOff.png"
|
||||
WinPhone="Assets/switchOff.png"/>
|
||||
</controls:ToggleButton.UnCheckedImage>
|
||||
</controls:ToggleButton>
|
||||
<!-- ENDPOINT -->
|
||||
<StackLayout
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
BackgroundColor="Gray"/>
|
||||
Margin="12, 0, 12, 12"
|
||||
IsVisible="{Binding UseFakeLocation}">
|
||||
<Label
|
||||
Text="Latitude"
|
||||
Style="{StaticResource HeaderLabelStyle}"/>
|
||||
<Entry
|
||||
Text="{Binding Latitude, Mode=TwoWay}"
|
||||
Keyboard="Numeric">
|
||||
<Entry.Style>
|
||||
<OnPlatform
|
||||
x:TypeArguments="Style"
|
||||
iOS="{StaticResource EntryStyle}"
|
||||
Android="{StaticResource EntryStyle}"
|
||||
WinPhone="{StaticResource UwpEntryStyle}"/>
|
||||
</Entry.Style>
|
||||
</Entry>
|
||||
<Label
|
||||
Text="Longitude"
|
||||
Style="{StaticResource HeaderLabelStyle}"/>
|
||||
<Entry
|
||||
Text="{Binding Longitude, Mode=TwoWay}"
|
||||
Keyboard="Numeric">
|
||||
<Entry.Style>
|
||||
<OnPlatform
|
||||
x:TypeArguments="Style"
|
||||
iOS="{StaticResource EntryStyle}"
|
||||
Android="{StaticResource EntryStyle}"
|
||||
WinPhone="{StaticResource UwpEntryStyle}"/>
|
||||
</Entry.Style>
|
||||
</Entry>
|
||||
<Button
|
||||
Command="{Binding ToggleSendLocationCommand}"
|
||||
Text="Send Location"/>
|
||||
</StackLayout>
|
||||
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
@ -70,6 +70,7 @@
|
||||
<Compile Include="Models\Catalog\CatalogItem.cs" />
|
||||
<Compile Include="Models\Catalog\CatalogRoot.cs" />
|
||||
<Compile Include="Models\Catalog\CatalogType.cs" />
|
||||
<Compile Include="Models\Location\LocationRequest.cs" />
|
||||
<Compile Include="Models\Navigation\TabParameter.cs" />
|
||||
<Compile Include="Models\Orders\CardType.CS" />
|
||||
<Compile Include="Models\Orders\Order.cs" />
|
||||
@ -91,6 +92,8 @@
|
||||
<Compile Include="Services\Dialog\IDialogService.cs" />
|
||||
<Compile Include="Services\Identity\IdentityService.cs" />
|
||||
<Compile Include="Services\Identity\IIdentityService.cs" />
|
||||
<Compile Include="Services\Location\ILocationService.cs" />
|
||||
<Compile Include="Services\Location\LocationService.cs" />
|
||||
<Compile Include="Services\Navigation\INavigationService.cs" />
|
||||
<Compile Include="Services\Navigation\NavigationService.cs" />
|
||||
<Compile Include="Services\OpenUrl\IOpenUrlService.cs" />
|
||||
|
@ -30,6 +30,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.2" />
|
||||
|
@ -13,7 +13,8 @@ namespace Identity.API.Configuration
|
||||
{
|
||||
new ApiResource("orders", "Orders Service"),
|
||||
new ApiResource("basket", "Basket Service"),
|
||||
new ApiResource("marketing", "Marketing Service")
|
||||
new ApiResource("marketing", "Marketing Service"),
|
||||
new ApiResource("locations", "Locations Service")
|
||||
};
|
||||
}
|
||||
|
||||
@ -72,7 +73,8 @@ namespace Identity.API.Configuration
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||
"orders",
|
||||
"basket"
|
||||
"basket",
|
||||
"locations"
|
||||
},
|
||||
//Allow requesting refresh tokens for long lived API access
|
||||
AllowOfflineAccess = true
|
||||
@ -104,7 +106,8 @@ namespace Identity.API.Configuration
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||
"orders",
|
||||
"basket"
|
||||
"basket",
|
||||
"locations"
|
||||
},
|
||||
}
|
||||
};
|
||||
|
3
src/Services/Location/Locations.API/.dockerignore
Normal file
3
src/Services/Location/Locations.API/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
*
|
||||
!obj/Docker/publish/*
|
||||
!obj/Docker/empty/
|
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
// GET: /<controller>/
|
||||
public IActionResult Index()
|
||||
{
|
||||
return new RedirectResult("~/swagger");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Locations.API.Controllers
|
||||
{
|
||||
[Route("api/v1/[controller]")]
|
||||
[Authorize]
|
||||
public class LocationsController : ControllerBase
|
||||
{
|
||||
private readonly ILocationsService _locationsService;
|
||||
private readonly IIdentityService _identityService;
|
||||
|
||||
public LocationsController(ILocationsService locationsService, IIdentityService identityService)
|
||||
{
|
||||
_locationsService = locationsService ?? throw new ArgumentNullException(nameof(locationsService));
|
||||
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
||||
}
|
||||
|
||||
//GET api/v1/[controller]/user/1
|
||||
[Route("user/{userId:int}")]
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetUserLocation(int userId)
|
||||
{
|
||||
var userLocation = await _locationsService.GetUserLocation(userId);
|
||||
return Ok(userLocation);
|
||||
}
|
||||
|
||||
//GET api/v1/[controller]/
|
||||
[Route("")]
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAllLocations()
|
||||
{
|
||||
var locations = await _locationsService.GetAllLocation();
|
||||
return Ok(locations);
|
||||
}
|
||||
|
||||
//GET api/v1/[controller]/1
|
||||
[Route("{locationId}")]
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetLocation(string locationId)
|
||||
{
|
||||
var location = await _locationsService.GetLocation(locationId);
|
||||
return Ok(location);
|
||||
}
|
||||
|
||||
//POST api/v1/[controller]/
|
||||
[Route("")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateOrUpdateUserLocation([FromBody]LocationRequest newLocReq)
|
||||
{
|
||||
var userId = _identityService.GetUserIdentity();
|
||||
var result = await _locationsService.AddOrUpdateUserLocation(userId, newLocReq);
|
||||
return result ?
|
||||
(IActionResult)Ok() :
|
||||
(IActionResult)BadRequest();
|
||||
}
|
||||
}
|
||||
}
|
6
src/Services/Location/Locations.API/Dockerfile
Normal file
6
src/Services/Location/Locations.API/Dockerfile
Normal file
@ -0,0 +1,6 @@
|
||||
FROM microsoft/aspnetcore:1.1
|
||||
ARG source
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
COPY ${source:-obj/Docker/publish} .
|
||||
ENTRYPOINT ["dotnet", "Locations.API.dll"]
|
@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.ActionResults
|
||||
{
|
||||
public class InternalServerErrorObjectResult : ObjectResult
|
||||
{
|
||||
public InternalServerErrorObjectResult(object error)
|
||||
: base(error)
|
||||
{
|
||||
StatusCode = StatusCodes.Status500InternalServerError;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Exception type for app exceptions
|
||||
/// </summary>
|
||||
public class LocationDomainException : Exception
|
||||
{
|
||||
public LocationDomainException()
|
||||
{ }
|
||||
|
||||
public LocationDomainException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
public LocationDomainException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Filters
|
||||
{
|
||||
using AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.ActionResults;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Net;
|
||||
|
||||
public class HttpGlobalExceptionFilter : IExceptionFilter
|
||||
{
|
||||
private readonly IHostingEnvironment env;
|
||||
private readonly ILogger<HttpGlobalExceptionFilter> logger;
|
||||
|
||||
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
|
||||
{
|
||||
this.env = env;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void OnException(ExceptionContext context)
|
||||
{
|
||||
logger.LogError(new EventId(context.Exception.HResult),
|
||||
context.Exception,
|
||||
context.Exception.Message);
|
||||
|
||||
if (context.Exception.GetType() == typeof(LocationDomainException))
|
||||
{
|
||||
var json = new JsonErrorResponse
|
||||
{
|
||||
Messages = new[] { context.Exception.Message }
|
||||
};
|
||||
|
||||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1
|
||||
//It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information
|
||||
context.Result = new BadRequestObjectResult(json);
|
||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
var json = new JsonErrorResponse
|
||||
{
|
||||
Messages = new[] { "An error occur.Try it again." }
|
||||
};
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
json.DeveloperMessage = context.Exception;
|
||||
}
|
||||
|
||||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1
|
||||
// It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information
|
||||
context.Result = new InternalServerErrorObjectResult(json);
|
||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
}
|
||||
context.ExceptionHandled = true;
|
||||
}
|
||||
|
||||
private class JsonErrorResponse
|
||||
{
|
||||
public string[] Messages { get; set; }
|
||||
|
||||
public object DeveloperMessage { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure
|
||||
{
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
|
||||
public class LocationsContext
|
||||
{
|
||||
private readonly IMongoDatabase _database = null;
|
||||
|
||||
public LocationsContext(IOptions<LocationSettings> settings)
|
||||
{
|
||||
var client = new MongoClient(settings.Value.ConnectionString);
|
||||
if (client != null)
|
||||
_database = client.GetDatabase(settings.Value.Database);
|
||||
}
|
||||
|
||||
public IMongoCollection<UserLocation> UserLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
return _database.GetCollection<UserLocation>("UserLocation");
|
||||
}
|
||||
}
|
||||
|
||||
public IMongoCollection<Locations> Locations
|
||||
{
|
||||
get
|
||||
{
|
||||
return _database.GetCollection<Locations>("Locations");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.GeoJsonObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class LocationsContextSeed
|
||||
{
|
||||
private static LocationsContext ctx;
|
||||
public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory)
|
||||
{
|
||||
var config = applicationBuilder
|
||||
.ApplicationServices.GetRequiredService<IOptions<LocationSettings>>();
|
||||
|
||||
ctx = new LocationsContext(config);
|
||||
|
||||
if (!ctx.Locations.Database.GetCollection<Locations>(nameof(Locations)).AsQueryable().Any())
|
||||
{
|
||||
await SetIndexes();
|
||||
await SetUSLocations();
|
||||
}
|
||||
}
|
||||
|
||||
static async Task SetUSLocations()
|
||||
{
|
||||
var us = new Locations()
|
||||
{
|
||||
Code = "US",
|
||||
Description = "United States"
|
||||
};
|
||||
us.SetLocation(-101.357386, 41.650455);
|
||||
us.SetArea(GetUSPoligon());
|
||||
await ctx.Locations.InsertOneAsync(us);
|
||||
await SetWashingtonLocations(us.Id);
|
||||
}
|
||||
|
||||
static async Task SetWashingtonLocations(string parentId)
|
||||
{
|
||||
var wht = new Locations()
|
||||
{
|
||||
Parent_Id = parentId,
|
||||
Code = "WHT",
|
||||
Description = "Washington"
|
||||
};
|
||||
wht.SetLocation(-119.542781, 47.223652);
|
||||
wht.SetArea(GetWashingtonPoligon());
|
||||
await ctx.Locations.InsertOneAsync(wht);
|
||||
await SetSeattleLocations(wht.Id);
|
||||
await SetRedmondLocations(wht.Id);
|
||||
}
|
||||
|
||||
static async Task SetSeattleLocations(string parentId)
|
||||
{
|
||||
var stl = new Locations()
|
||||
{
|
||||
Parent_Id = parentId,
|
||||
Code = "SEAT",
|
||||
Description = "Seattle"
|
||||
};
|
||||
stl.SetArea(GetSeattlePoligon());
|
||||
stl.SetLocation(-122.330747, 47.603111);
|
||||
await ctx.Locations.InsertOneAsync(stl);
|
||||
}
|
||||
|
||||
static async Task SetRedmondLocations(string parentId)
|
||||
{
|
||||
var rdm = new Locations()
|
||||
{
|
||||
Parent_Id = parentId,
|
||||
Code = "REDM",
|
||||
Description = "Redmond"
|
||||
};
|
||||
rdm.SetLocation(-122.122887, 47.674961);
|
||||
rdm.SetArea(GetRedmondPoligon());
|
||||
await ctx.Locations.InsertOneAsync(rdm);
|
||||
}
|
||||
|
||||
static async Task SetIndexes()
|
||||
{
|
||||
// Set location indexes
|
||||
var builder = Builders<Locations>.IndexKeys;
|
||||
var keys = builder.Geo2DSphere(prop => prop.Location);
|
||||
await ctx.Locations.Indexes.CreateOneAsync(keys);
|
||||
}
|
||||
|
||||
static List<GeoJson2DGeographicCoordinates> GetUSPoligon()
|
||||
{
|
||||
return new List<GeoJson2DGeographicCoordinates>()
|
||||
{
|
||||
new GeoJson2DGeographicCoordinates(-62.88205, 48.7985),
|
||||
new GeoJson2DGeographicCoordinates(-129.3132, 48.76513),
|
||||
new GeoJson2DGeographicCoordinates(-120.9496, 30.12256),
|
||||
new GeoJson2DGeographicCoordinates(-111.3944, 30.87114),
|
||||
new GeoJson2DGeographicCoordinates(-78.11975, 24.24979),
|
||||
new GeoJson2DGeographicCoordinates(-62.88205, 48.7985)
|
||||
};
|
||||
}
|
||||
|
||||
static List<GeoJson2DGeographicCoordinates> GetSeattlePoligon()
|
||||
{
|
||||
return new List<GeoJson2DGeographicCoordinates>()
|
||||
{
|
||||
new GeoJson2DGeographicCoordinates(-122.36238,47.82929),
|
||||
new GeoJson2DGeographicCoordinates(-122.42091,47.6337),
|
||||
new GeoJson2DGeographicCoordinates(-122.37371,47.45224),
|
||||
new GeoJson2DGeographicCoordinates(-122.20788,47.50259),
|
||||
new GeoJson2DGeographicCoordinates(-122.26934,47.73644),
|
||||
new GeoJson2DGeographicCoordinates(-122.36238,47.82929)
|
||||
};
|
||||
}
|
||||
|
||||
static List<GeoJson2DGeographicCoordinates> GetRedmondPoligon()
|
||||
{
|
||||
return new List<GeoJson2DGeographicCoordinates>()
|
||||
{
|
||||
new GeoJson2DGeographicCoordinates(-122.15432, 47.73148),
|
||||
new GeoJson2DGeographicCoordinates(-122.17673, 47.72559),
|
||||
new GeoJson2DGeographicCoordinates(-122.16904, 47.67851),
|
||||
new GeoJson2DGeographicCoordinates(-122.16136, 47.65036),
|
||||
new GeoJson2DGeographicCoordinates(-122.15604, 47.62746),
|
||||
new GeoJson2DGeographicCoordinates(-122.01562, 47.63463),
|
||||
new GeoJson2DGeographicCoordinates(-122.04961, 47.74244),
|
||||
new GeoJson2DGeographicCoordinates(-122.15432, 47.73148)
|
||||
};
|
||||
}
|
||||
|
||||
static List<GeoJson2DGeographicCoordinates> GetWashingtonPoligon()
|
||||
{
|
||||
return new List<GeoJson2DGeographicCoordinates>()
|
||||
{
|
||||
new GeoJson2DGeographicCoordinates(-124.68633, 48.8943),
|
||||
new GeoJson2DGeographicCoordinates(-124.32962, 45.66613),
|
||||
new GeoJson2DGeographicCoordinates(-116.73824, 45.93384),
|
||||
new GeoJson2DGeographicCoordinates(-116.96912, 49.04282),
|
||||
new GeoJson2DGeographicCoordinates(-124.68633, 48.8943)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories
|
||||
{
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
|
||||
using ViewModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public interface ILocationsRepository
|
||||
{
|
||||
Task<Locations> GetAsync(string locationId);
|
||||
|
||||
Task<List<Locations>> GetLocationListAsync();
|
||||
|
||||
Task<UserLocation> GetUserLocationAsync(int userId);
|
||||
|
||||
Task<List<Locations>> GetCurrentUserRegionsListAsync(LocationRequest currentPosition);
|
||||
|
||||
Task AddUserLocationAsync(UserLocation location);
|
||||
|
||||
Task UpdateUserLocationAsync(UserLocation userLocation);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories
|
||||
{
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.GeoJsonObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using ViewModel;
|
||||
|
||||
public class LocationsRepository
|
||||
: ILocationsRepository
|
||||
{
|
||||
private readonly LocationsContext _context;
|
||||
|
||||
public LocationsRepository(IOptions<LocationSettings> settings)
|
||||
{
|
||||
_context = new LocationsContext(settings);
|
||||
}
|
||||
|
||||
public async Task<Locations> GetAsync(string locationId)
|
||||
{
|
||||
var filter = Builders<Locations>.Filter.Eq("Id", ObjectId.Parse(locationId));
|
||||
return await _context.Locations
|
||||
.Find(filter)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<UserLocation> GetUserLocationAsync(int userId)
|
||||
{
|
||||
var filter = Builders<UserLocation>.Filter.Eq("UserId", userId);
|
||||
return await _context.UserLocation
|
||||
.Find(filter)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Locations>> GetLocationListAsync()
|
||||
{
|
||||
return await _context.Locations.Find(new BsonDocument()).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Locations>> GetCurrentUserRegionsListAsync(LocationRequest currentPosition)
|
||||
{
|
||||
var point = GeoJson.Point(GeoJson.Geographic(currentPosition.Longitude, currentPosition.Latitude));
|
||||
var orderByDistanceQuery = new FilterDefinitionBuilder<Locations>().Near(x => x.Location, point);
|
||||
var withinAreaQuery = new FilterDefinitionBuilder<Locations>().GeoIntersects("Polygon", point);
|
||||
var filter = Builders<Locations>.Filter.And(orderByDistanceQuery, withinAreaQuery);
|
||||
return await _context.Locations.Find(filter).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task AddUserLocationAsync(UserLocation location)
|
||||
{
|
||||
await _context.UserLocation.InsertOneAsync(location);
|
||||
}
|
||||
|
||||
public async Task UpdateUserLocationAsync(UserLocation userLocation)
|
||||
{
|
||||
await _context.UserLocation.ReplaceOneAsync(
|
||||
doc => doc.UserId == userLocation.UserId,
|
||||
userLocation,
|
||||
new UpdateOptions { IsUpsert = true });
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services
|
||||
{
|
||||
public interface IIdentityService
|
||||
{
|
||||
string GetUserIdentity();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services
|
||||
{
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public interface ILocationsService
|
||||
{
|
||||
Task<Locations> GetLocation(string locationId);
|
||||
|
||||
Task<UserLocation> GetUserLocation(int id);
|
||||
|
||||
Task<List<Locations>> GetAllLocation();
|
||||
|
||||
Task<bool> AddOrUpdateUserLocation(string userId, LocationRequest locRequest);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services
|
||||
{
|
||||
public class IdentityService : IIdentityService
|
||||
{
|
||||
private IHttpContextAccessor _context;
|
||||
|
||||
public IdentityService(IHttpContextAccessor context)
|
||||
{
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
public string GetUserIdentity()
|
||||
{
|
||||
return _context.HttpContext.User.FindFirst("sub").Value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services
|
||||
{
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class LocationsService : ILocationsService
|
||||
{
|
||||
private ILocationsRepository _locationsRepository;
|
||||
|
||||
public LocationsService(ILocationsRepository locationsRepository)
|
||||
{
|
||||
_locationsRepository = locationsRepository ?? throw new ArgumentNullException(nameof(locationsRepository));
|
||||
}
|
||||
|
||||
public async Task<Locations> GetLocation(string locationId)
|
||||
{
|
||||
return await _locationsRepository.GetAsync(locationId);
|
||||
}
|
||||
|
||||
public async Task<UserLocation> GetUserLocation(int id)
|
||||
{
|
||||
return await _locationsRepository.GetUserLocationAsync(id);
|
||||
}
|
||||
|
||||
public async Task<List<Locations>> GetAllLocation()
|
||||
{
|
||||
return await _locationsRepository.GetLocationListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> AddOrUpdateUserLocation(string id, LocationRequest currentPosition)
|
||||
{
|
||||
if (!int.TryParse(id, out int userId))
|
||||
{
|
||||
throw new ArgumentException("Not valid userId");
|
||||
}
|
||||
|
||||
// Get the list of ordered regions the user currently is within
|
||||
var currentUserAreaLocationList = await _locationsRepository.GetCurrentUserRegionsListAsync(currentPosition);
|
||||
|
||||
if(currentUserAreaLocationList is null)
|
||||
{
|
||||
throw new LocationDomainException("User current area not found");
|
||||
}
|
||||
|
||||
// If current area found, then update user location
|
||||
var locationAncestors = new List<string>();
|
||||
var userLocation = await _locationsRepository.GetUserLocationAsync(userId);
|
||||
userLocation = userLocation ?? new UserLocation();
|
||||
userLocation.UserId = userId;
|
||||
userLocation.LocationId = currentUserAreaLocationList[0].Id;
|
||||
userLocation.UpdateDate = DateTime.UtcNow;
|
||||
await _locationsRepository.UpdateUserLocationAsync(userLocation);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
15
src/Services/Location/Locations.API/LocationSettings.cs
Normal file
15
src/Services/Location/Locations.API/LocationSettings.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API
|
||||
{
|
||||
public class LocationSettings
|
||||
{
|
||||
public string ExternalCatalogBaseUrl { get; set; }
|
||||
public string EventBusConnection { get; set; }
|
||||
public string ConnectionString { get; set; }
|
||||
public string Database { get; set; }
|
||||
}
|
||||
}
|
41
src/Services/Location/Locations.API/Locations.API.csproj
Normal file
41
src/Services/Location/Locations.API/Locations.API.csproj
Normal file
@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
<RootNamespace>Microsoft.eShopOnContainers.Services.Locations.API</RootNamespace>
|
||||
<UserSecretsId>aspnet-Locations.API-20161122013619</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
|
||||
<PackageReference Include="mongocsharpdriver" Version="2.4.3" />
|
||||
<PackageReference Include="MongoDB.Bson" Version="2.4.3" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.3" />
|
||||
<PackageReference Include="MongoDB.Driver.Core" Version="2.4.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
37
src/Services/Location/Locations.API/Model/Locations.cs
Normal file
37
src/Services/Location/Locations.API/Model/Locations.cs
Normal file
@ -0,0 +1,37 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Model
|
||||
{
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver.GeoJsonObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
public class Locations
|
||||
{
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
public string Code { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Parent_Id { get; set; }
|
||||
public string Description { get; set; }
|
||||
public double Latitude { get; set; }
|
||||
public double Longitude { get; set; }
|
||||
public GeoJsonPoint<GeoJson2DGeographicCoordinates> Location { get; private set; }
|
||||
public GeoJsonPolygon<GeoJson2DGeographicCoordinates> Polygon { get; private set; }
|
||||
public void SetLocation(double lon, double lat) => SetPosition(lon, lat);
|
||||
public void SetArea(List<GeoJson2DGeographicCoordinates> coordinatesList) => SetPolygon(coordinatesList);
|
||||
|
||||
private void SetPosition(double lon, double lat)
|
||||
{
|
||||
Latitude = lat;
|
||||
Longitude = lon;
|
||||
Location = new GeoJsonPoint<GeoJson2DGeographicCoordinates>(
|
||||
new GeoJson2DGeographicCoordinates(lon, lat));
|
||||
}
|
||||
|
||||
private void SetPolygon(List<GeoJson2DGeographicCoordinates> coordinatesList)
|
||||
{
|
||||
Polygon = new GeoJsonPolygon<GeoJson2DGeographicCoordinates>(new GeoJsonPolygonCoordinates<GeoJson2DGeographicCoordinates>(
|
||||
new GeoJsonLinearRingCoordinates<GeoJson2DGeographicCoordinates>(coordinatesList)));
|
||||
}
|
||||
}
|
||||
}
|
17
src/Services/Location/Locations.API/Model/UserLocation.cs
Normal file
17
src/Services/Location/Locations.API/Model/UserLocation.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Model
|
||||
{
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using System;
|
||||
|
||||
public class UserLocation
|
||||
{
|
||||
[BsonIgnoreIfDefault]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
public int UserId { get; set; } = 0;
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string LocationId { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
}
|
||||
}
|
25
src/Services/Location/Locations.API/Program.cs
Normal file
25
src/Services/Location/Locations.API/Program.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>()
|
||||
.UseApplicationInsights()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:3278/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/values",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Locations.API": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/values",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:3279"
|
||||
}
|
||||
}
|
||||
}
|
113
src/Services/Location/Locations.API/Startup.cs
Normal file
113
src/Services/Location/Locations.API/Startup.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Filters;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
public Startup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
builder.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly);
|
||||
}
|
||||
|
||||
builder.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Add framework services.
|
||||
services.AddMvc(options =>
|
||||
{
|
||||
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
|
||||
}).AddControllersAsServices();
|
||||
|
||||
services.Configure<LocationSettings>(Configuration);
|
||||
|
||||
// Add framework services.
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.DescribeAllEnumsAsStrings();
|
||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
|
||||
{
|
||||
Title = "eShopOnContainers - Location HTTP API",
|
||||
Version = "v1",
|
||||
Description = "The Location Microservice HTTP API. This is a Data-Driven/CRUD microservice sample",
|
||||
TermsOfService = "Terms Of Service"
|
||||
});
|
||||
});
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("CorsPolicy",
|
||||
builder => builder.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader()
|
||||
.AllowCredentials());
|
||||
});
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
services.AddTransient<IIdentityService, IdentityService>();
|
||||
services.AddTransient<ILocationsService, LocationsService>();
|
||||
services.AddTransient<ILocationsRepository, LocationsRepository>();
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
//Configure logs
|
||||
|
||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||
loggerFactory.AddDebug();
|
||||
|
||||
app.UseCors("CorsPolicy");
|
||||
|
||||
ConfigureAuth(app);
|
||||
|
||||
app.UseMvcWithDefaultRoute();
|
||||
|
||||
app.UseSwagger()
|
||||
.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||
});
|
||||
|
||||
LocationsContextSeed.SeedAsync(app, loggerFactory)
|
||||
.Wait();
|
||||
}
|
||||
|
||||
protected virtual void ConfigureAuth(IApplicationBuilder app)
|
||||
{
|
||||
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
|
||||
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
|
||||
{
|
||||
Authority = identityUrl.ToString(),
|
||||
ApiName = "locations",
|
||||
RequireHttpsMetadata = false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Locations.API.ViewModel
|
||||
{
|
||||
public class LocationRequest
|
||||
{
|
||||
public double Longitude { get; set; }
|
||||
public double Latitude { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
13
src/Services/Location/Locations.API/appsettings.json
Normal file
13
src/Services/Location/Locations.API/appsettings.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"ConnectionString": "mongodb://nosql.data",
|
||||
"Database": "LocationsDb",
|
||||
"IdentityUrl": "http://localhost:5105",
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Services\Catalog\settings.json" />
|
||||
<None Remove="Services\Locations\appsettings.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -23,6 +24,9 @@
|
||||
<Content Include="Services\Marketing\appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Services\Locations\appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Services\Ordering\settings.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
@ -31,6 +35,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Services\Basket\Basket.API\Basket.API.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Services\Catalog\Catalog.API\Catalog.API.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Services\Location\Locations.API\Locations.API.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Services\Marketing\Marketing.API\Marketing.API.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Services\Ordering\Ordering.API\Ordering.API.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace IntegrationTests.Services.Locations
|
||||
{
|
||||
public class LocationsScenarioBase
|
||||
{
|
||||
public TestServer CreateServer()
|
||||
{
|
||||
var webHostBuilder = new WebHostBuilder();
|
||||
webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory() + "\\Services\\Locations");
|
||||
webHostBuilder.UseStartup<LocationsTestsStartup>();
|
||||
|
||||
return new TestServer(webHostBuilder);
|
||||
}
|
||||
|
||||
public static class Get
|
||||
{
|
||||
public static string Locations = "api/v1/locations";
|
||||
|
||||
public static string LocationBy(string id)
|
||||
{
|
||||
return $"api/v1/locations/{id}";
|
||||
}
|
||||
|
||||
public static string UserLocationBy(int id)
|
||||
{
|
||||
return $"api/v1/locations/user/{id}";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Post
|
||||
{
|
||||
public static string AddNewLocation = "api/v1/locations/";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel;
|
||||
using Location = Microsoft.eShopOnContainers.Services.Locations.API.Model.Locations;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace IntegrationTests.Services.Locations
|
||||
{
|
||||
public class LocationsScenarios
|
||||
: LocationsScenarioBase
|
||||
{
|
||||
[Fact]
|
||||
public async Task Set_new_user_seattle_location_response_ok_status_code()
|
||||
{
|
||||
using (var server = CreateServer())
|
||||
{
|
||||
var userId = 1234;
|
||||
var content = new StringContent(BuildLocationsRequest(-122.315752, 47.604610), UTF8Encoding.UTF8, "application/json");
|
||||
|
||||
// Expected result
|
||||
var expectedLocation = "SEAT";
|
||||
|
||||
// Act
|
||||
var response = await server.CreateClient()
|
||||
.PostAsync(Post.AddNewLocation, content);
|
||||
|
||||
var userLocationResponse = await server.CreateClient()
|
||||
.GetAsync(Get.UserLocationBy(userId));
|
||||
|
||||
var responseBody = await userLocationResponse.Content.ReadAsStringAsync();
|
||||
var userLocation = JsonConvert.DeserializeObject<UserLocation>(responseBody);
|
||||
|
||||
var locationResponse = await server.CreateClient()
|
||||
.GetAsync(Get.LocationBy(userLocation.LocationId));
|
||||
|
||||
responseBody = await locationResponse.Content.ReadAsStringAsync();
|
||||
var location = JsonConvert.DeserializeObject<Location>(responseBody);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedLocation, location.Code);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Set_new_user_readmond_location_response_ok_status_code()
|
||||
{
|
||||
using (var server = CreateServer())
|
||||
{
|
||||
var userId = 1234;
|
||||
var content = new StringContent(BuildLocationsRequest(-122.119998, 47.690876), UTF8Encoding.UTF8, "application/json");
|
||||
|
||||
// Expected result
|
||||
var expectedLocation = "REDM";
|
||||
|
||||
// Act
|
||||
var response = await server.CreateClient()
|
||||
.PostAsync(Post.AddNewLocation, content);
|
||||
|
||||
var userLocationResponse = await server.CreateClient()
|
||||
.GetAsync(Get.UserLocationBy(userId));
|
||||
|
||||
var responseBody = await userLocationResponse.Content.ReadAsStringAsync();
|
||||
var userLocation = JsonConvert.DeserializeObject<UserLocation>(responseBody);
|
||||
|
||||
var locationResponse = await server.CreateClient()
|
||||
.GetAsync(Get.LocationBy(userLocation.LocationId));
|
||||
|
||||
responseBody = await locationResponse.Content.ReadAsStringAsync();
|
||||
var location = JsonConvert.DeserializeObject<Location>(responseBody);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedLocation, location.Code);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Set_new_user_Washington_location_response_ok_status_code()
|
||||
{
|
||||
using (var server = CreateServer())
|
||||
{
|
||||
var userId = 1234;
|
||||
var content = new StringContent(BuildLocationsRequest(-121.040360, 48.091631), UTF8Encoding.UTF8, "application/json");
|
||||
|
||||
// Expected result
|
||||
var expectedLocation = "WHT";
|
||||
|
||||
// Act
|
||||
var response = await server.CreateClient()
|
||||
.PostAsync(Post.AddNewLocation, content);
|
||||
|
||||
var userLocationResponse = await server.CreateClient()
|
||||
.GetAsync(Get.UserLocationBy(userId));
|
||||
|
||||
var responseBody = await userLocationResponse.Content.ReadAsStringAsync();
|
||||
var userLocation = JsonConvert.DeserializeObject<UserLocation>(responseBody);
|
||||
|
||||
var locationResponse = await server.CreateClient()
|
||||
.GetAsync(Get.LocationBy(userLocation.LocationId));
|
||||
|
||||
responseBody = await locationResponse.Content.ReadAsStringAsync();
|
||||
var location = JsonConvert.DeserializeObject<Location>(responseBody);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedLocation, location.Code);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_all_locations_response_ok_status_code()
|
||||
{
|
||||
using (var server = CreateServer())
|
||||
{
|
||||
var response = await server.CreateClient()
|
||||
.GetAsync(Get.Locations);
|
||||
|
||||
var responseBody = await response.Content.ReadAsStringAsync();
|
||||
var locations = JsonConvert.DeserializeObject<List<Location>>(responseBody);
|
||||
|
||||
// Assert
|
||||
Assert.NotEmpty(locations);
|
||||
}
|
||||
}
|
||||
|
||||
string BuildLocationsRequest(double lon, double lat)
|
||||
{
|
||||
var location = new LocationRequest()
|
||||
{
|
||||
Longitude = lon,
|
||||
Latitude = lat
|
||||
};
|
||||
return JsonConvert.SerializeObject(location);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
namespace IntegrationTests.Services.Locations
|
||||
{
|
||||
using IntegrationTests.Middleware;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.eShopOnContainers.Services.Locations.API;
|
||||
|
||||
public class LocationsTestsStartup : Startup
|
||||
{
|
||||
public LocationsTestsStartup(IHostingEnvironment env) : base(env)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ConfigureAuth(IApplicationBuilder app)
|
||||
{
|
||||
if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant())
|
||||
{
|
||||
app.UseMiddleware<AutoAuthorizeMiddleware>();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ConfigureAuth(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"ConnectionString": "mongodb://localhost:27017",
|
||||
"Database": "LocationsDb",
|
||||
"ExternalCatalogBaseUrl": "http://localhost:5101",
|
||||
"IdentityUrl": "http://localhost:5105",
|
||||
"isTest": "true",
|
||||
"EventBusConnection": "localhost"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user