diff --git a/Core/Resgrid.Model/Repositories/IPersonnelLocationsDocRepository.cs b/Core/Resgrid.Model/Repositories/IPersonnelLocationsDocRepository.cs index 3464fb8a..edc444a4 100644 --- a/Core/Resgrid.Model/Repositories/IPersonnelLocationsDocRepository.cs +++ b/Core/Resgrid.Model/Repositories/IPersonnelLocationsDocRepository.cs @@ -11,5 +11,6 @@ public interface IPersonnelLocationsDocRepository Task GetByIdAsync(string id); Task GetByOldIdAsync(string id); Task InsertAsync(PersonnelLocation location); + Task UpdateAsync(PersonnelLocation location); } } diff --git a/Core/Resgrid.Model/Repositories/IUnitLocationsDocRepository.cs b/Core/Resgrid.Model/Repositories/IUnitLocationsDocRepository.cs index 3fb3b9c5..0e30eba6 100644 --- a/Core/Resgrid.Model/Repositories/IUnitLocationsDocRepository.cs +++ b/Core/Resgrid.Model/Repositories/IUnitLocationsDocRepository.cs @@ -11,5 +11,6 @@ public interface IUnitLocationsDocRepository Task GetByIdAsync(string id); Task GetByOldIdAsync(string id); Task InsertAsync(UnitsLocation location); + Task UpdateAsync(UnitsLocation location); } } diff --git a/Core/Resgrid.Services/MappingService.cs b/Core/Resgrid.Services/MappingService.cs index b88c09b7..95702159 100644 --- a/Core/Resgrid.Services/MappingService.cs +++ b/Core/Resgrid.Services/MappingService.cs @@ -13,10 +13,10 @@ public class MappingService : IMappingService { private readonly IPoiTypesRepository _poiTypesRepository; private readonly IPoisRepository _poisRepository; - private readonly IMongoRepository _mapLayersRepository; + private readonly Lazy> _mapLayersRepository; private readonly IMapLayersDocRepository _mapLayersDocRepository; - public MappingService(IPoiTypesRepository poiTypesRepository, IPoisRepository poisRepository, IMongoRepository mapLayersRepository, + public MappingService(IPoiTypesRepository poiTypesRepository, IPoisRepository poisRepository, Lazy> mapLayersRepository, IMapLayersDocRepository mapLayersDocRepository) { _poiTypesRepository = poiTypesRepository; @@ -75,9 +75,9 @@ public async Task SaveMapLayerAsync(MapLayer mapLayer) else { if (mapLayer.Id.Timestamp == 0) - await _mapLayersRepository.InsertOneAsync(mapLayer); + await _mapLayersRepository.Value.InsertOneAsync(mapLayer); else - await _mapLayersRepository.ReplaceOneAsync(mapLayer); + await _mapLayersRepository.Value.ReplaceOneAsync(mapLayer); return mapLayer; } @@ -93,7 +93,7 @@ public async Task> GetMapLayersForTypeDepartmentAsync(int departm } else { - var layers = await _mapLayersRepository.FilterByAsync(filter => filter.DepartmentId == departmentId && filter.Type == (int)type && filter.IsDeleted == false); + var layers = await _mapLayersRepository.Value.FilterByAsync(filter => filter.DepartmentId == departmentId && filter.Type == (int)type && filter.IsDeleted == false); return layers.ToList(); } @@ -109,7 +109,7 @@ public async Task GetMapLayersByIdAsync(string id) } else { - var layers = await _mapLayersRepository.FindByIdAsync(id); + var layers = await _mapLayersRepository.Value.FindByIdAsync(id); return layers; } diff --git a/Core/Resgrid.Services/UnitsService.cs b/Core/Resgrid.Services/UnitsService.cs index c4ef4c2e..000e7b48 100644 --- a/Core/Resgrid.Services/UnitsService.cs +++ b/Core/Resgrid.Services/UnitsService.cs @@ -20,7 +20,8 @@ public class UnitsService : IUnitsService private readonly IUnitTypesRepository _unitTypesRepository; private readonly IUnitRolesRepository _unitRolesRepository; private readonly IUnitStateRoleRepository _unitStateRoleRepository; - private readonly IMongoRepository _unitLocationRepository; + private readonly Lazy> _unitLocationRepository; + private readonly IUnitLocationsDocRepository _unitLocationsDocRepository; private readonly ISubscriptionsService _subscriptionsService; private readonly IUserStateService _userStateService; private readonly IEventAggregator _eventAggregator; @@ -32,8 +33,9 @@ public class UnitsService : IUnitsService public UnitsService(IUnitsRepository unitsRepository, IUnitStatesRepository unitStatesRepository, IUnitLogsRepository unitLogsRepository, IUnitTypesRepository unitTypesRepository, ISubscriptionsService subscriptionsService, IUnitRolesRepository unitRolesRepository, IUnitStateRoleRepository unitStateRoleRepository, IUserStateService userStateService, - IEventAggregator eventAggregator, ICustomStateService customStateService, IMongoRepository unitLocationRepository, - IUnitActiveRolesRepository unitActiveRolesRepository, IDepartmentGroupsService departmentGroupsService, ILimitsService limitsService) + IEventAggregator eventAggregator, ICustomStateService customStateService, Lazy> unitLocationRepository, + IUnitLocationsDocRepository unitLocationsDocRepository, IUnitActiveRolesRepository unitActiveRolesRepository, + IDepartmentGroupsService departmentGroupsService, ILimitsService limitsService) { _unitsRepository = unitsRepository; _unitStatesRepository = unitStatesRepository; @@ -46,6 +48,7 @@ public UnitsService(IUnitsRepository unitsRepository, IUnitStatesRepository unit _eventAggregator = eventAggregator; _customStateService = customStateService; _unitLocationRepository = unitLocationRepository; + _unitLocationsDocRepository = unitLocationsDocRepository; _unitActiveRolesRepository = unitActiveRolesRepository; _departmentGroupsService = departmentGroupsService; _limitsService = limitsService; @@ -528,10 +531,20 @@ where callEnabledStates.Contains(us.State) { try { - if (location.Id.Timestamp == 0) - await _unitLocationRepository.InsertOneAsync(location); + if (Config.DataConfig.DocDatabaseType == Config.DatabaseTypes.Postgres) + { + if (String.IsNullOrWhiteSpace(location.PgId)) + location = await _unitLocationsDocRepository.InsertAsync(location); + else + location = await _unitLocationsDocRepository.UpdateAsync(location); + } else - await _unitLocationRepository.ReplaceOneAsync(location); + { + if (location.Id.Timestamp == 0) + await _unitLocationRepository.Value.InsertOneAsync(location); + else + await _unitLocationRepository.Value.ReplaceOneAsync(location); + } _eventAggregator.SendMessage(new UnitLocationUpdatedEvent() { @@ -539,7 +552,7 @@ where callEnabledStates.Contains(us.State) UnitId = location.UnitId.ToString(), Latitude = double.Parse(location.Latitude.ToString()), Longitude = double.Parse(location.Longitude.ToString()), - RecordId = location.Id.ToString(), + RecordId = location.GetId(), }); } catch (Exception ex) @@ -554,7 +567,10 @@ public async Task GetLatestUnitLocationAsync(int unitId, DateTime { try { - var location = _unitLocationRepository.AsQueryable().Where(x => x.UnitId == unitId).OrderByDescending(y => y.Timestamp).FirstOrDefault(); + if (Config.DataConfig.DocDatabaseType == Config.DatabaseTypes.Postgres) + return await _unitLocationsDocRepository.GetLatestLocationsByUnitIdAsync(unitId); + + var location = _unitLocationRepository.Value.AsQueryable().Where(x => x.UnitId == unitId).OrderByDescending(y => y.Timestamp).FirstOrDefault(); //var layers = await _personnelLocationRepository.FilterByAsync(filter => filter.DepartmentId == departmentId && filter.Type == (int)type && filter.IsDeleted == false); @@ -572,10 +588,12 @@ public async Task> GetLatestUnitLocationsAsync(int departmen { try { + if (Config.DataConfig.DocDatabaseType == Config.DatabaseTypes.Postgres) + return await _unitLocationsDocRepository.GetLatestLocationsByDepartmentIdAsync(departmentId); //var locations = _unitLocationRepository.AsQueryable().Where(x => x.DepartmentId == departmentId).OrderByDescending(y => y.Timestamp).GroupBy(x => x.UnitId);//.FirstOrDefault(); - var locations = _unitLocationRepository + var locations = _unitLocationRepository.Value .AsQueryable() .Where(x => x.DepartmentId == departmentId).OrderByDescending(y => y.Timestamp) .GroupBy(pv => pv.UnitId, (key, group) => new diff --git a/Core/Resgrid.Services/UsersService.cs b/Core/Resgrid.Services/UsersService.cs index 45a37ec4..78b26b2f 100644 --- a/Core/Resgrid.Services/UsersService.cs +++ b/Core/Resgrid.Services/UsersService.cs @@ -38,13 +38,15 @@ public string AffiliateRoleId private readonly ICacheProvider _cacheProvider; private readonly IIdentityRepository _identityRepository; private readonly IDepartmentSettingsService _departmentSettingsService; - private readonly IMongoRepository _personnelLocationRepository; + private readonly Lazy> _personnelLocationRepository; + private readonly IPersonnelLocationsDocRepository _personnelLocationsDocRepository; private readonly IEventAggregator _eventAggregator; private readonly ILimitsService _limitsService; public UsersService(IDepartmentMembersRepository departmentMembersRepository, ICacheProvider cacheProvider, IIdentityRepository identityRepository, IDepartmentSettingsService departmentSettingsService, - IMongoRepository personnelLocationRepository, IEventAggregator eventAggregator, + Lazy> personnelLocationRepository, IPersonnelLocationsDocRepository personnelLocationsDocRepository, + IEventAggregator eventAggregator, ILimitsService limitsService) { _departmentMembersRepository = departmentMembersRepository; @@ -52,6 +54,7 @@ public UsersService(IDepartmentMembersRepository departmentMembersRepository, IC _identityRepository = identityRepository; _departmentSettingsService = departmentSettingsService; _personnelLocationRepository = personnelLocationRepository; + _personnelLocationsDocRepository = personnelLocationsDocRepository; _eventAggregator = eventAggregator; _limitsService = limitsService; } @@ -257,17 +260,27 @@ public async Task SavePersonnelLocationAsync(PersonnelLocatio { try { - if (personnelLocation.Id.Timestamp == 0) - await _personnelLocationRepository.InsertOneAsync(personnelLocation); + if (Config.DataConfig.DocDatabaseType == Config.DatabaseTypes.Postgres) + { + if (String.IsNullOrWhiteSpace(personnelLocation.PgId)) + personnelLocation = await _personnelLocationsDocRepository.InsertAsync(personnelLocation); + else + personnelLocation = await _personnelLocationsDocRepository.UpdateAsync(personnelLocation); + } else - await _personnelLocationRepository.ReplaceOneAsync(personnelLocation); + { + if (personnelLocation.Id.Timestamp == 0) + await _personnelLocationRepository.Value.InsertOneAsync(personnelLocation); + else + await _personnelLocationRepository.Value.ReplaceOneAsync(personnelLocation); + } _eventAggregator.SendMessage(new PersonnelLocationUpdatedEvent() { DepartmentId = personnelLocation.DepartmentId, UserId = personnelLocation.UserId, Latitude = personnelLocation.Latitude, Longitude = personnelLocation.Longitude, - RecordId = personnelLocation.Id.ToString(), + RecordId = personnelLocation.GetId(), }); } catch (Exception ex) @@ -282,7 +295,10 @@ public async Task> GetLatestLocationsForDepartmentPerson { try { - var locations = _personnelLocationRepository + if (Config.DataConfig.DocDatabaseType == Config.DatabaseTypes.Postgres) + return await _personnelLocationsDocRepository.GetLatestLocationsByDepartmentIdAsync(departmentId); + + var locations = _personnelLocationRepository.Value .AsQueryable() .Where(x => x.DepartmentId == departmentId).OrderByDescending(y => y.Timestamp) .GroupBy(pv => pv.UserId, (key, group) => new @@ -308,7 +324,10 @@ public async Task GetPersonnelLocationByIdAsync(string id) { try { - var layers = await _personnelLocationRepository.FindByIdAsync(id); + if (Config.DataConfig.DocDatabaseType == Config.DatabaseTypes.Postgres) + return await _personnelLocationsDocRepository.GetByIdAsync(id); + + var layers = await _personnelLocationRepository.Value.FindByIdAsync(id); return layers; } diff --git a/Repositories/Resgrid.Repositories.NoSqlRepository/PersonnelLocationsDocRepository.cs b/Repositories/Resgrid.Repositories.NoSqlRepository/PersonnelLocationsDocRepository.cs index 6c0712c8..287da39c 100644 --- a/Repositories/Resgrid.Repositories.NoSqlRepository/PersonnelLocationsDocRepository.cs +++ b/Repositories/Resgrid.Repositories.NoSqlRepository/PersonnelLocationsDocRepository.cs @@ -18,7 +18,9 @@ public async Task> GetAllLocationsByUnitIdAsync(string u using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var personLocationsData = await connection.QueryAsync($"SELECT data FROM public.personnellocations ul WHERE ul.userid = '{userId}' ORDER BY timestamp DESC;"); + var personLocationsData = await connection.QueryAsync( + "SELECT data FROM public.personnellocations ul WHERE ul.userid = @userId ORDER BY timestamp DESC;", + new { userId }); if (personLocationsData != null) return personLocationsData.ToList(); @@ -32,7 +34,9 @@ public async Task GetLatestLocationsByUnitIdAsync(string user using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT data FROM public.personnellocations ul WHERE ul.userid = '{userId}' ORDER BY timestamp DESC LIMIT 1;"); + var unitLocationsData = await connection.QueryAsync( + "SELECT data FROM public.personnellocations ul WHERE ul.userid = @userId ORDER BY timestamp DESC LIMIT 1;", + new { userId }); if (unitLocationsData != null) return unitLocationsData.FirstOrDefault(); @@ -46,7 +50,9 @@ public async Task> GetLatestLocationsByDepartmentIdAsync using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT DISTINCT ON (userid) data FROM public.personnellocations ul WHERE ul.departmentid = {departmentId} ORDER BY timestamp DESC;"); + var unitLocationsData = await connection.QueryAsync( + "SELECT DISTINCT ON (userid) data FROM public.personnellocations ul WHERE ul.departmentid = @departmentId ORDER BY ul.userid, ul.timestamp DESC;", + new { departmentId }); if (unitLocationsData != null) return unitLocationsData.ToList(); @@ -60,19 +66,24 @@ public async Task GetByIdAsync(string id) using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT data FROM public.personnellocations ul WHERE ul.oid = '{id}';"); + var unitLocationsData = await connection.QueryAsync( + "SELECT data FROM public.personnellocations ul WHERE ul.oid = @id;", + new { id }); - if (unitLocationsData != null) + if (unitLocationsData != null && unitLocationsData.Any()) return unitLocationsData.FirstOrDefault(); + + if (!int.TryParse(id, out var numericId)) + return null; + + var unitLocationsData2 = await connection.QueryAsync( + "SELECT data FROM public.personnellocations ul WHERE ul.id = @id;", + new { id = numericId }); + + if (unitLocationsData2 != null && unitLocationsData2.Any()) + return unitLocationsData2.FirstOrDefault(); else - { - var unitLocationsData2 = await connection.QueryAsync($"SELECT data FROM public.personnellocations ul WHERE ul.id = {id};"); - - if (unitLocationsData2 != null) - return unitLocationsData2.FirstOrDefault(); - else - return null; - } + return null; } } @@ -81,7 +92,9 @@ public async Task GetByOldIdAsync(string id) using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var personnelLocationsData = await connection.QueryAsync($"SELECT data FROM public.personnellocations ul WHERE ul.oid = '{id}';"); + var personnelLocationsData = await connection.QueryAsync( + "SELECT data FROM public.personnellocations ul WHERE ul.oid = @id;", + new { id }); if (personnelLocationsData != null) return personnelLocationsData.FirstOrDefault(); @@ -92,14 +105,54 @@ public async Task GetByOldIdAsync(string id) public async Task InsertAsync(PersonnelLocation location) { + var dataJson = JsonConvert.SerializeObject(location); + using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var result = await connection.ExecuteScalarAsync($"INSERT INTO public.personnellocations (departmentid, userid, data) VALUES ({location.DepartmentId}, '{location.UserId}', '{JsonConvert.SerializeObject(location)}') RETURNING id;"); + var result = await connection.ExecuteScalarAsync( + "INSERT INTO public.personnellocations (departmentid, userid, data) VALUES (@departmentId, @userId, CAST(@dataJson AS jsonb)) RETURNING id::text;", + new + { + departmentId = location.DepartmentId, + userId = location.UserId, + dataJson + }); location.PgId = result; return location; } } + + public async Task UpdateAsync(PersonnelLocation location) + { + if (location == null) + throw new ArgumentNullException(nameof(location)); + + if (string.IsNullOrWhiteSpace(location.PgId)) + throw new InvalidOperationException("Personnel location PgId is required for updates."); + + if (!int.TryParse(location.PgId, out var pgId)) + throw new ArgumentException("Personnel location PgId must be a valid integer.", nameof(location)); + + var dataJson = JsonConvert.SerializeObject(location); + + using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) + { + await connection.OpenAsync(); + + await connection.ExecuteAsync( + "UPDATE public.personnellocations SET departmentid = @departmentId, userid = @userId, data = CAST(@dataJson AS jsonb) WHERE id = @id;", + new + { + departmentId = location.DepartmentId, + userId = location.UserId, + dataJson, + id = pgId + }); + + return location; + } + } } } diff --git a/Repositories/Resgrid.Repositories.NoSqlRepository/UnitLocationsDocRepository.cs b/Repositories/Resgrid.Repositories.NoSqlRepository/UnitLocationsDocRepository.cs index dd0f887e..444c7be0 100644 --- a/Repositories/Resgrid.Repositories.NoSqlRepository/UnitLocationsDocRepository.cs +++ b/Repositories/Resgrid.Repositories.NoSqlRepository/UnitLocationsDocRepository.cs @@ -18,7 +18,9 @@ public async Task> GetAllLocationsByUnitIdAsync(int unitId) using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT data FROM public.unitlocations ul WHERE ul.unitid = {unitId} ORDER BY timestamp DESC;"); + var unitLocationsData = await connection.QueryAsync( + "SELECT data FROM public.unitlocations ul WHERE ul.unitid = @unitId ORDER BY timestamp DESC;", + new { unitId }); if (unitLocationsData != null) return unitLocationsData.ToList(); @@ -32,7 +34,9 @@ public async Task GetLatestLocationsByUnitIdAsync(int unitId) using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT data FROM public.unitlocations ul WHERE ul.unitid = {unitId} ORDER BY timestamp DESC LIMIT 1;"); + var unitLocationsData = await connection.QueryAsync( + "SELECT data FROM public.unitlocations ul WHERE ul.unitid = @unitId ORDER BY timestamp DESC LIMIT 1;", + new { unitId }); if (unitLocationsData != null) return unitLocationsData.FirstOrDefault(); @@ -46,7 +50,9 @@ public async Task> GetLatestLocationsByDepartmentIdAsync(int using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT DISTINCT ON (unitid) data FROM public.unitlocations ul WHERE ul.departmentid = {departmentId} ORDER BY timestamp DESC;"); + var unitLocationsData = await connection.QueryAsync( + "SELECT DISTINCT ON (unitid) data FROM public.unitlocations ul WHERE ul.departmentid = @departmentId ORDER BY ul.unitid, ul.timestamp DESC;", + new { departmentId }); if (unitLocationsData != null) return unitLocationsData.ToList(); @@ -60,19 +66,24 @@ public async Task GetByIdAsync(string id) using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT data FROM public.unitlocations ul WHERE ul.oid = '{id}';"); + var unitLocationsData = await connection.QueryAsync( + "SELECT data FROM public.unitlocations ul WHERE ul.oid = @id;", + new { id }); - if (unitLocationsData != null) + if (unitLocationsData != null && unitLocationsData.Any()) return unitLocationsData.FirstOrDefault(); + + if (!int.TryParse(id, out var numericId)) + return null; + + var unitLocationsData2 = await connection.QueryAsync( + "SELECT data FROM public.unitlocations ul WHERE ul.id = @id;", + new { id = numericId }); + + if (unitLocationsData2 != null && unitLocationsData2.Any()) + return unitLocationsData2.FirstOrDefault(); else - { - var unitLocationsData2 = await connection.QueryAsync($"SELECT data FROM public.unitlocations ul WHERE ul.id = {id};"); - - if (unitLocationsData2 != null) - return unitLocationsData2.FirstOrDefault(); - else - return null; - } + return null; } } @@ -81,7 +92,9 @@ public async Task GetByOldIdAsync(string id) using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var unitLocationsData = await connection.QueryAsync($"SELECT data FROM public.unitlocations ul WHERE ul.oid = '{id}';"); + var unitLocationsData = await connection.QueryAsync( + "SELECT data FROM public.unitlocations ul WHERE ul.oid = @id;", + new { id }); if (unitLocationsData != null) return unitLocationsData.FirstOrDefault(); @@ -92,14 +105,57 @@ public async Task GetByOldIdAsync(string id) public async Task InsertAsync(UnitsLocation location) { + if (location == null) + throw new ArgumentNullException(nameof(location)); + + var dataJson = JsonConvert.SerializeObject(location); + using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) { await connection.OpenAsync(); - var result = await connection.ExecuteScalarAsync($"INSERT INTO public.unitlocations (departmentid, unitid, data) VALUES ({location.DepartmentId}, {location.UnitId}, '{JsonConvert.SerializeObject(location)}') RETURNING id;"); + var result = await connection.ExecuteScalarAsync( + "INSERT INTO public.unitlocations (departmentid, unitid, data) VALUES (@departmentId, @unitId, CAST(@dataJson AS jsonb)) RETURNING id::text;", + new + { + departmentId = location.DepartmentId, + unitId = location.UnitId, + dataJson + }); location.PgId = result; return location; } } + + public async Task UpdateAsync(UnitsLocation location) + { + if (location == null) + throw new ArgumentNullException(nameof(location)); + + if (string.IsNullOrWhiteSpace(location.PgId)) + throw new InvalidOperationException("Unit location PgId is required for updates."); + + if (!int.TryParse(location.PgId, out var pgId)) + throw new ArgumentException("Unit location PgId must be a valid integer.", nameof(location)); + + var dataJson = JsonConvert.SerializeObject(location); + + using (var connection = new NpgsqlConnection(Config.DataConfig.DocumentConnectionString)) + { + await connection.OpenAsync(); + + await connection.ExecuteAsync( + "UPDATE public.unitlocations SET departmentid = @departmentId, unitid = @unitId, data = CAST(@dataJson AS jsonb) WHERE id = @id;", + new + { + departmentId = location.DepartmentId, + unitId = location.UnitId, + dataJson, + id = pgId + }); + + return location; + } + } } } diff --git a/Tests/Resgrid.Tests/Services/DocumentDatabaseProviderSelectionTests.cs b/Tests/Resgrid.Tests/Services/DocumentDatabaseProviderSelectionTests.cs new file mode 100644 index 00000000..e6b55ef0 --- /dev/null +++ b/Tests/Resgrid.Tests/Services/DocumentDatabaseProviderSelectionTests.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using Resgrid.Config; +using Resgrid.Model; +using Resgrid.Model.Events; +using Resgrid.Model.Providers; +using Resgrid.Model.Repositories; +using Resgrid.Model.Services; +using Resgrid.Services; + +namespace Resgrid.Tests.Services +{ + [TestFixture] + public class DocumentDatabaseProviderSelectionTests + { + private DatabaseTypes _originalDocDatabaseType; + + [SetUp] + public void SetUp() + { + _originalDocDatabaseType = DataConfig.DocDatabaseType; + } + + [TearDown] + public void TearDown() + { + DataConfig.DocDatabaseType = _originalDocDatabaseType; + } + + [Test] + public async Task GetLatestUnitLocationsAsync_should_use_postgres_doc_repository_when_doc_database_is_postgres() + { + DataConfig.DocDatabaseType = DatabaseTypes.Postgres; + + var expected = new List + { + new UnitsLocation { DepartmentId = 7, UnitId = 12, PgId = "42" } + }; + + var eventAggregator = new Mock(); + var unitLocationsDocRepository = new Mock(); + unitLocationsDocRepository.Setup(x => x.GetLatestLocationsByDepartmentIdAsync(7)).ReturnsAsync(expected); + + var service = CreateUnitsService( + eventAggregator.Object, + new Lazy>(() => throw new InvalidOperationException("Mongo repository should not be resolved in Postgres mode.")), + unitLocationsDocRepository.Object); + + var result = await service.GetLatestUnitLocationsAsync(7); + + result.Should().BeEquivalentTo(expected); + unitLocationsDocRepository.Verify(x => x.GetLatestLocationsByDepartmentIdAsync(7), Times.Once); + } + + [Test] + public async Task AddUnitLocationAsync_should_publish_postgres_record_id_when_doc_database_is_postgres() + { + DataConfig.DocDatabaseType = DatabaseTypes.Postgres; + + var eventAggregator = new Mock(); + var unitLocationsDocRepository = new Mock(); + unitLocationsDocRepository + .Setup(x => x.InsertAsync(It.IsAny())) + .ReturnsAsync((UnitsLocation location) => + { + location.PgId = "314"; + return location; + }); + + var service = CreateUnitsService( + eventAggregator.Object, + new Lazy>(() => throw new InvalidOperationException("Mongo repository should not be resolved in Postgres mode.")), + unitLocationsDocRepository.Object); + + var location = new UnitsLocation + { + DepartmentId = 7, + UnitId = 12, + Latitude = 39.7392m, + Longitude = -104.9903m, + Timestamp = DateTime.UtcNow + }; + + var result = await service.AddUnitLocationAsync(location, 7); + + result.PgId.Should().Be("314"); + unitLocationsDocRepository.Verify(x => x.InsertAsync(location), Times.Once); + eventAggregator.Verify( + x => x.SendMessage(It.Is(e => e.RecordId == "314" && e.UnitId == "12")), + Times.Once); + } + + [Test] + public async Task GetLatestLocationsForDepartmentPersonnelAsync_should_use_postgres_doc_repository_when_doc_database_is_postgres() + { + DataConfig.DocDatabaseType = DatabaseTypes.Postgres; + + var expected = new List + { + new PersonnelLocation { DepartmentId = 7, UserId = "user-1", PgId = "77" } + }; + + var personnelLocationsDocRepository = new Mock(); + personnelLocationsDocRepository.Setup(x => x.GetLatestLocationsByDepartmentIdAsync(7)).ReturnsAsync(expected); + + var service = CreateUsersService( + new Mock().Object, + new Lazy>(() => throw new InvalidOperationException("Mongo repository should not be resolved in Postgres mode.")), + personnelLocationsDocRepository.Object); + + var result = await service.GetLatestLocationsForDepartmentPersonnelAsync(7); + + result.Should().BeEquivalentTo(expected); + personnelLocationsDocRepository.Verify(x => x.GetLatestLocationsByDepartmentIdAsync(7), Times.Once); + } + + [Test] + public async Task SavePersonnelLocationAsync_should_publish_postgres_record_id_when_doc_database_is_postgres() + { + DataConfig.DocDatabaseType = DatabaseTypes.Postgres; + + var eventAggregator = new Mock(); + var personnelLocationsDocRepository = new Mock(); + personnelLocationsDocRepository + .Setup(x => x.InsertAsync(It.IsAny())) + .ReturnsAsync((PersonnelLocation location) => + { + location.PgId = "512"; + return location; + }); + + var service = CreateUsersService( + eventAggregator.Object, + new Lazy>(() => throw new InvalidOperationException("Mongo repository should not be resolved in Postgres mode.")), + personnelLocationsDocRepository.Object); + + var location = new PersonnelLocation + { + DepartmentId = 7, + UserId = "user-1", + Latitude = 39.7392m, + Longitude = -104.9903m, + Timestamp = DateTime.UtcNow + }; + + var result = await service.SavePersonnelLocationAsync(location); + + result.PgId.Should().Be("512"); + personnelLocationsDocRepository.Verify(x => x.InsertAsync(location), Times.Once); + eventAggregator.Verify( + x => x.SendMessage(It.Is(e => e.RecordId == "512" && e.UserId == "user-1")), + Times.Once); + } + + [Test] + public async Task GetMapLayersForTypeDepartmentAsync_should_not_resolve_mongo_repository_when_doc_database_is_postgres() + { + DataConfig.DocDatabaseType = DatabaseTypes.Postgres; + + var expected = new List + { + new MapLayer { DepartmentId = 7, PgId = "9001", Type = (int)MapLayerTypes.TopLevel } + }; + + var mapLayersDocRepository = new Mock(); + mapLayersDocRepository.Setup(x => x.GetAllMapLayersByDepartmentIdAsync(7, MapLayerTypes.TopLevel)).ReturnsAsync(expected); + + var service = new MappingService( + new Mock().Object, + new Mock().Object, + new Lazy>(() => throw new InvalidOperationException("Mongo repository should not be resolved in Postgres mode.")), + mapLayersDocRepository.Object); + + var result = await service.GetMapLayersForTypeDepartmentAsync(7, MapLayerTypes.TopLevel); + + result.Should().BeEquivalentTo(expected); + mapLayersDocRepository.Verify(x => x.GetAllMapLayersByDepartmentIdAsync(7, MapLayerTypes.TopLevel), Times.Once); + } + + private static UnitsService CreateUnitsService(IEventAggregator eventAggregator, Lazy> mongoRepository, IUnitLocationsDocRepository unitLocationsDocRepository) + { + return new UnitsService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + eventAggregator, + new Mock().Object, + mongoRepository, + unitLocationsDocRepository, + new Mock().Object, + new Mock().Object, + new Mock().Object); + } + + private static UsersService CreateUsersService(IEventAggregator eventAggregator, Lazy> mongoRepository, IPersonnelLocationsDocRepository personnelLocationsDocRepository) + { + return new UsersService( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + mongoRepository, + personnelLocationsDocRepository, + eventAggregator, + new Mock().Object); + } + } +} diff --git a/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs b/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs index 6497b183..58590210 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/SubscriptionController.cs @@ -828,7 +828,7 @@ public async Task PaddleProcessing(int planId) ProcessingView model = new ProcessingView(); model.PlanId = planId; - return View(model); + return View("Processing", model); } [HttpGet] diff --git a/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml b/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml index eee60984..d2d3eaab 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml @@ -677,7 +677,7 @@ if (data.TransactionId) { ensurePaddleInitialized().then(function () { Paddle.Checkout.open({ - settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, + settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/Processing?planId=' + id }, transactionId: data.TransactionId }); }).catch(function (error) { @@ -696,7 +696,7 @@ } var checkoutSettings = { - settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, + settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/Processing?planId=' + id }, items: [{ priceId: data.PriceId, quantity: packs }] }; diff --git a/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml b/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml index 25b35f69..3ee0f7ec 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Subscription/SelectRegistrationPlan.cshtml @@ -285,7 +285,7 @@ if (data.TransactionId) { ensurePaddleInitialized().then(function () { Paddle.Checkout.open({ - settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, + settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/Processing?planId=' + id }, transactionId: data.TransactionId }); }).catch(function (error) { @@ -302,7 +302,7 @@ return; } var checkoutSettings = { - settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/PaddleProcessing?planId=' + id }, + settings: { successUrl: resgrid.absoluteBaseUrl + '/User/Subscription/Processing?planId=' + id }, items: [{ priceId: data.PriceId, quantity: packs }] }; if (data.CustomerId) {