Browse Source

Added location.api integration tests

pull/809/head
Ramón Tomás 7 years ago
parent
commit
ea6084db28
12 changed files with 250 additions and 102 deletions
  1. +16
    -1
      docker-compose-windows.override.yml
  2. +12
    -1
      docker-compose-windows.yml
  3. +16
    -7
      src/Services/Location/Locations.API/Controllers/LocationsController.cs
  4. +52
    -30
      src/Services/Location/Locations.API/Infrastructure/LocationsContextSeed.cs
  5. +3
    -5
      src/Services/Location/Locations.API/Infrastructure/Repositories/ILocationsRepository.cs
  6. +15
    -22
      src/Services/Location/Locations.API/Infrastructure/Repositories/LocationsRepository.cs
  7. +2
    -0
      src/Services/Location/Locations.API/Infrastructure/Services/ILocationsService.cs
  8. +17
    -24
      src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs
  9. +14
    -4
      src/Services/Location/Locations.API/Model/Locations.cs
  10. +5
    -4
      src/Services/Location/Locations.API/Model/UserLocation.cs
  11. +6
    -1
      test/Services/IntegrationTests/Services/Locations/LocationsScenarioBase.cs
  12. +92
    -3
      test/Services/IntegrationTests/Services/Locations/LocationsScenarios.cs

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

@ -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"

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

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


+ 16
- 7
src/Services/Location/Locations.API/Controllers/LocationsController.cs View File

@ -20,8 +20,8 @@ namespace Locations.API.Controllers
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
}
//GET api/v1/[controller]/1
[Route("{userId:int}")]
//GET api/v1/[controller]/user/1
[Route("user/{userId:int}")]
[HttpGet]
public async Task<IActionResult> GetUserLocation(int userId)
{
@ -29,19 +29,28 @@ namespace Locations.API.Controllers
return Ok(userLocation);
}
//GET api/v1/[controller]/locations
[Route("locations")]
//GET api/v1/[controller]/
[Route("")]
[HttpGet]
public async Task<IActionResult> GetAllLocations()
{
var userLocation = await _locationsService.GetAllLocation();
return Ok(userLocation);
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> UpdateUserLocation([FromBody]LocationRequest newLocReq)
public async Task<IActionResult> CreateOrUpdateUserLocation([FromBody]LocationRequest newLocReq)
{
var userId = _identityService.GetUserIdentity();
var result = await _locationsService.AddOrUpdateUserLocation(userId, newLocReq);


+ 52
- 30
src/Services/Location/Locations.API/Infrastructure/LocationsContextSeed.cs View File

@ -5,7 +5,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.GeoJsonObjectModel;
using System.Collections.Generic;
@ -25,7 +24,7 @@
{
await SetIndexes();
await SetUSLocations();
}
}
}
static async Task SetUSLocations()
@ -36,11 +35,12 @@
Description = "United States"
};
us.SetLocation(-101.357386, 41.650455);
await ctx.Locations.InsertOneAsync(us);
us.SetArea(GetUSPoligon());
await ctx.Locations.InsertOneAsync(us);
await SetWashingtonLocations(us.Id);
}
static async Task SetWashingtonLocations(ObjectId parentId)
static async Task SetWashingtonLocations(string parentId)
{
var wht = new Locations()
{
@ -49,34 +49,35 @@
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(ObjectId parentId)
static async Task SetSeattleLocations(string parentId)
{
var stl = new Locations()
{
Parent_Id = parentId,
Code = "SEAT",
Description = "Seattle",
Polygon = GetSeattlePoligon()
Description = "Seattle"
};
stl.SetArea(GetSeattlePoligon());
stl.SetLocation(-122.330747, 47.603111);
await ctx.Locations.InsertOneAsync(stl);
}
static async Task SetRedmondLocations(ObjectId parentId)
static async Task SetRedmondLocations(string parentId)
{
var rdm = new Locations()
{
Parent_Id = parentId,
Code = "REDM",
Description = "Redmond",
Polygon = GetRedmondPoligon()
Description = "Redmond"
};
rdm.SetLocation(-122.122887, 47.674961);
rdm.SetArea(GetRedmondPoligon());
await ctx.Locations.InsertOneAsync(rdm);
}
@ -86,13 +87,24 @@
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 GeoJsonPolygon<GeoJson2DGeographicCoordinates> GetSeattlePoligon()
static List<GeoJson2DGeographicCoordinates> GetSeattlePoligon()
{
return new GeoJsonPolygon<GeoJson2DGeographicCoordinates>(new GeoJsonPolygonCoordinates<GeoJson2DGeographicCoordinates>(
new GeoJsonLinearRingCoordinates<GeoJson2DGeographicCoordinates>(
new List<GeoJson2DGeographicCoordinates>()
return new List<GeoJson2DGeographicCoordinates>()
{
new GeoJson2DGeographicCoordinates(-122.36238,47.82929),
new GeoJson2DGeographicCoordinates(-122.42091,47.6337),
@ -100,24 +112,34 @@
new GeoJson2DGeographicCoordinates(-122.20788,47.50259),
new GeoJson2DGeographicCoordinates(-122.26934,47.73644),
new GeoJson2DGeographicCoordinates(-122.36238,47.82929)
})));
};
}
static GeoJsonPolygon<GeoJson2DGeographicCoordinates> GetRedmondPoligon()
static List<GeoJson2DGeographicCoordinates> GetRedmondPoligon()
{
return new GeoJsonPolygon<GeoJson2DGeographicCoordinates>(new GeoJsonPolygonCoordinates<GeoJson2DGeographicCoordinates>(
new GeoJsonLinearRingCoordinates<GeoJson2DGeographicCoordinates>(
new List<GeoJson2DGeographicCoordinates>()
return new List<GeoJson2DGeographicCoordinates>()
{
new GeoJson2DGeographicCoordinates(47.73148, -122.15432),
new GeoJson2DGeographicCoordinates(47.72559, -122.17673),
new GeoJson2DGeographicCoordinates(47.67851, -122.16904),
new GeoJson2DGeographicCoordinates(47.65036, -122.16136),
new GeoJson2DGeographicCoordinates(47.62746, -122.15604),
new GeoJson2DGeographicCoordinates(47.63463, -122.01562),
new GeoJson2DGeographicCoordinates(47.74244, -122.04961),
new GeoJson2DGeographicCoordinates(47.73148, -122.15432),
})));
}
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)
};
}
}
}

+ 3
- 5
src/Services/Location/Locations.API/Infrastructure/Repositories/ILocationsRepository.cs View File

@ -1,21 +1,19 @@
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories
{
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
using MongoDB.Bson;
using ViewModel;
using System.Collections.Generic;
using System.Threading.Tasks;
public interface ILocationsRepository
{
Task<Locations> GetAsync(ObjectId locationId);
Task<Locations> GetAsync(string locationId);
Task<List<Locations>> GetLocationListAsync();
Task<UserLocation> GetUserLocationAsync(int userId);
Task<List<Locations>> GetNearestLocationListAsync(double lat, double lon);
Task<Locations> GetLocationByCurrentAreaAsync(Locations location);
Task<List<Locations>> GetCurrentUserRegionsListAsync(LocationRequest currentPosition);
Task AddUserLocationAsync(UserLocation location);


+ 15
- 22
src/Services/Location/Locations.API/Infrastructure/Repositories/LocationsRepository.cs View File

@ -1,17 +1,14 @@
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories
{
using System;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.GeoJsonObjectModel;
using MongoDB.Driver.Builders;
using MongoDB.Bson;
using System.Collections.Generic;
using System.Threading.Tasks;
using ViewModel;
public class LocationsRepository
: ILocationsRepository
@ -23,9 +20,9 @@
_context = new LocationsContext(settings);
}
public async Task<Locations> GetAsync(ObjectId locationId)
public async Task<Locations> GetAsync(string locationId)
{
var filter = Builders<Locations>.Filter.Eq("Id", locationId);
var filter = Builders<Locations>.Filter.Eq("Id", ObjectId.Parse(locationId));
return await _context.Locations
.Find(filter)
.FirstOrDefaultAsync();
@ -42,20 +39,16 @@
public async Task<List<Locations>> GetLocationListAsync()
{
return await _context.Locations.Find(new BsonDocument()).ToListAsync();
}
}
public async Task<List<Locations>> GetNearestLocationListAsync(double lat, double lon)
public async Task<List<Locations>> GetCurrentUserRegionsListAsync(LocationRequest currentPosition)
{
var point = GeoJson.Point(GeoJson.Geographic(lon, lat));
var query = new FilterDefinitionBuilder<Locations>().Near(x => x.Location, point);
return await _context.Locations.Find(query).ToListAsync();
}
public async Task<Locations> GetLocationByCurrentAreaAsync(Locations location)
{
var query = new FilterDefinitionBuilder<Locations>().GeoIntersects("Location", location.Polygon);
return await _context.Locations.Find(query).FirstOrDefaultAsync();
}
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)
{


+ 2
- 0
src/Services/Location/Locations.API/Infrastructure/Services/ILocationsService.cs View File

@ -7,6 +7,8 @@
public interface ILocationsService
{
Task<Locations> GetLocation(string locationId);
Task<UserLocation> GetUserLocation(int id);
Task<List<Locations>> GetAllLocation();


+ 17
- 24
src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs View File

@ -18,6 +18,11 @@
_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);
@ -30,40 +35,28 @@
public async Task<bool> AddOrUpdateUserLocation(string id, LocationRequest currentPosition)
{
Locations currentUserAreaLocation = null;
if (!int.TryParse(id, out int userId))
{
throw new ArgumentException("Not valid userId");
}
// Get the nearest locations ordered
var nearestLocationList = await _locationsRepository.GetNearestLocationListAsync(currentPosition.Latitude, currentPosition.Longitude);
// Check out in which region we currently are
foreach(var locationCandidate in nearestLocationList.Where(x=> x.Polygon != null))
{
currentUserAreaLocation = await _locationsRepository.GetLocationByCurrentAreaAsync(locationCandidate);
if(currentUserAreaLocation != null) { break; }
}
if(currentUserAreaLocation is null)
// 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
if(currentUserAreaLocation != null)
{
var locationAncestors = new List<string>();
var userLocation = await _locationsRepository.GetUserLocationAsync(userId);
userLocation = userLocation ?? new UserLocation();
userLocation.UserId = userId;
userLocation.LocationId = currentUserAreaLocation.Id;
userLocation.UpdateDate = DateTime.UtcNow;
await _locationsRepository.UpdateUserLocationAsync(userLocation);
}
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;
}
}


+ 14
- 4
src/Services/Location/Locations.API/Model/Locations.cs View File

@ -3,18 +3,22 @@
using MongoDB.Bson;
using MongoDB.Driver.GeoJsonObjectModel;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
public class Locations
{
public ObjectId Id { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Code { get; set; }
public ObjectId Parent_Id { 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; set; }
public GeoJsonPolygon<GeoJson2DGeographicCoordinates> Polygon { 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)
{
@ -23,5 +27,11 @@
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)));
}
}
}

+ 5
- 4
src/Services/Location/Locations.API/Model/UserLocation.cs View File

@ -1,16 +1,17 @@
namespace Microsoft.eShopOnContainers.Services.Locations.API.Model
{
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System;
using System.Collections.Generic;
public class UserLocation
{
[BsonIgnoreIfDefault]
public ObjectId Id { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int UserId { get; set; } = 0;
public ObjectId LocationId { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
public string LocationId { get; set; }
public DateTime UpdateDate { get; set; }
}
}

+ 6
- 1
test/Services/IntegrationTests/Services/Locations/LocationsScenarioBase.cs View File

@ -22,10 +22,15 @@ namespace IntegrationTests.Services.Locations
{
public static string Locations = "api/v1/locations";
public static string LocationBy(int id)
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


+ 92
- 3
test/Services/IntegrationTests/Services/Locations/LocationsScenarios.cs View File

@ -1,5 +1,7 @@
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;
@ -19,20 +21,107 @@ namespace IntegrationTests.Services.Locations
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);
response.EnsureSuccessStatusCode();
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.LocationBy(userId));
.GetAsync(Get.UserLocationBy(userId));
var responseBody = await userLocationResponse.Content.ReadAsStringAsync();
var userLocation = JsonConvert.DeserializeObject<UserLocation>(responseBody);
response.EnsureSuccessStatusCode();
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);
}
}


Loading…
Cancel
Save