From 2d4b51f9f14c368facac84b5807afd6efa826af2 Mon Sep 17 00:00:00 2001 From: Alexandre Brandizzi Date: Wed, 26 Aug 2026 14:50:16 -0300 Subject: [PATCH] fix: omit unmapped sites from work order options --- .../LocationControllerSitesTests.cs | 48 +++++++++++++++++-- .../Controllers/LocationController.cs | 6 ++- .../Implementation/LocationDataService.cs | 10 ++-- .../Interfaces/ILocationDataService.cs | 4 +- .../Implementation/LocationService.cs | 6 ++- .../Interfaces/ILocationService.cs | 4 +- .../WorkOrderAccountScopeTests.cs | 6 ++- 7 files changed, 69 insertions(+), 15 deletions(-) diff --git a/Api.SeaHavenIndustries.Tests/LocationControllerSitesTests.cs b/Api.SeaHavenIndustries.Tests/LocationControllerSitesTests.cs index ebbd3d6..3d2d209 100644 --- a/Api.SeaHavenIndustries.Tests/LocationControllerSitesTests.cs +++ b/Api.SeaHavenIndustries.Tests/LocationControllerSitesTests.cs @@ -46,10 +46,10 @@ public class LocationControllerSitesTests private static async Task SeedSitesAsync(ApplicationDbContext ctx) { ctx.Locations.AddRange( - new Locations { Id = 1, Name = "STL8", City = "St. Louis", State = "MO" }, - new Locations { Id = 2, Name = "BK5", City = "Dallas", State = "TX" }, - new Locations { Id = 3, Name = "IND9", City = "Indianapolis", State = "IN" }, - new Locations { Id = 4, Name = "DFW8", City = "Dallas", State = "TX" }); + new Locations { Id = 1, Name = "STL8", City = "St. Louis", State = "MO", AccountId = 100 }, + new Locations { Id = 2, Name = "BK5", City = "Dallas", State = "TX", AccountId = 100 }, + new Locations { Id = 3, Name = "IND9", City = "Indianapolis", State = "IN", AccountId = 100 }, + new Locations { Id = 4, Name = "DFW8", City = "Dallas", State = "TX", AccountId = 100 }); await ctx.SaveChangesAsync(); } @@ -107,4 +107,44 @@ public class LocationControllerSitesTests data.Should().HaveCount(2); data.Select(s => s.Code).Should().BeEquivalentTo("BK5", "DFW8"); } + + [Fact] + public async Task GetSites_OmitsLocationsWithoutAccountMapping() + { + await using var ctx = NewContext(); + ctx.Locations.AddRange( + new Locations { Id = 1, Name = "STL8", City = "St. Louis", State = "MO", AccountId = 100 }, + new Locations { Id = 2, Name = "UNMAPPED1", City = "Nowhere", State = "ZZ" }); + await ctx.SaveChangesAsync(); + var controller = NewController(ctx); + + var result = await controller.GetSites(); + + var ok = result.Should().BeOfType().Subject; + var data = ok.Value.Should().BeAssignableTo>().Subject; + + data.Should().ContainSingle(); + data[0].Code.Should().Be("STL8"); + + var searchResult = await controller.GetSites("unmapped1"); + + var searchOk = searchResult.Should().BeOfType().Subject; + var searchData = searchOk.Value.Should().BeAssignableTo>().Subject; + + searchData.Should().BeEmpty(); + } + + [Fact] + public async Task GetSites_ForwardsCancellationToken() + { + await using var ctx = NewContext(); + await SeedSitesAsync(ctx); + var controller = NewController(ctx); + using var cancellation = new CancellationTokenSource(); + await cancellation.CancelAsync(); + + var act = () => controller.GetSites(cancellationToken: cancellation.Token); + + await act.Should().ThrowAsync(); + } } diff --git a/Api.SeaHavenIndustries/Controllers/LocationController.cs b/Api.SeaHavenIndustries/Controllers/LocationController.cs index 977bdda..34ec79d 100644 --- a/Api.SeaHavenIndustries/Controllers/LocationController.cs +++ b/Api.SeaHavenIndustries/Controllers/LocationController.cs @@ -26,9 +26,11 @@ namespace Api.SeaHavenIndustries.Controllers } [HttpGet("sites")] - public async Task GetSites(string? search = null) + public async Task GetSites( + string? search = null, + CancellationToken cancellationToken = default) { - var data = await _locationService.GetSiteOptionsAsync(search); + var data = await _locationService.GetSiteOptionsAsync(search, cancellationToken); return Ok(data); } diff --git a/SeaHaven.DataServices/Implementation/LocationDataService.cs b/SeaHaven.DataServices/Implementation/LocationDataService.cs index 48ade48..7a589e7 100644 --- a/SeaHaven.DataServices/Implementation/LocationDataService.cs +++ b/SeaHaven.DataServices/Implementation/LocationDataService.cs @@ -74,9 +74,13 @@ namespace SeaHaven.DataServices.Implementation return (items, totalCount); } - public async Task> GetSiteOptionsAsync(string? search = null) + public async Task> GetSiteOptionsAsync( + string? search = null, + CancellationToken cancellationToken = default) { - var query = _context.Locations.AsNoTracking(); + var query = _context.Locations + .AsNoTracking() + .Where(l => l.AccountId != null); if (!string.IsNullOrWhiteSpace(search)) { @@ -89,7 +93,7 @@ namespace SeaHaven.DataServices.Implementation return await query .OrderBy(l => l.Name) .Select(l => new SiteOptionRow(l.Id, l.Name, l.City, l.State)) - .ToListAsync(); + .ToListAsync(cancellationToken); } public async Task AddAsync(Locations location) diff --git a/SeaHaven.DataServices/Interfaces/ILocationDataService.cs b/SeaHaven.DataServices/Interfaces/ILocationDataService.cs index 1bba950..8e7a51a 100644 --- a/SeaHaven.DataServices/Interfaces/ILocationDataService.cs +++ b/SeaHaven.DataServices/Interfaces/ILocationDataService.cs @@ -17,7 +17,9 @@ namespace SeaHaven.DataServices.Interfaces // For addressbook endpoint - returns simplified DTO Task<(IEnumerable Items, int TotalCount)> GetAddressbookPagedAsync(int page, int pageSize, string? search = null); - Task> GetSiteOptionsAsync(string? search = null); + Task> GetSiteOptionsAsync( + string? search = null, + CancellationToken cancellationToken = default); /// /// Exists is false when the row is missing. AccountId is null when the site has no account. diff --git a/SeaHaven.Services/Implementation/LocationService.cs b/SeaHaven.Services/Implementation/LocationService.cs index 6be9d0f..4af5a48 100644 --- a/SeaHaven.Services/Implementation/LocationService.cs +++ b/SeaHaven.Services/Implementation/LocationService.cs @@ -64,9 +64,11 @@ namespace SeaHaven.Services.Implementation }; } - public async Task> GetSiteOptionsAsync(string? search = null) + public async Task> GetSiteOptionsAsync( + string? search = null, + CancellationToken cancellationToken = default) { - var rows = await _locationDataService.GetSiteOptionsAsync(search); + var rows = await _locationDataService.GetSiteOptionsAsync(search, cancellationToken); return rows .Select(r => new SiteOptionDTO { diff --git a/SeaHaven.Services/Interfaces/ILocationService.cs b/SeaHaven.Services/Interfaces/ILocationService.cs index 21d9120..8d96f5c 100644 --- a/SeaHaven.Services/Interfaces/ILocationService.cs +++ b/SeaHaven.Services/Interfaces/ILocationService.cs @@ -9,7 +9,9 @@ namespace SeaHaven.Services.Interfaces Task> GetAllLocationsAsync(); Task> GetLocationsByAccountIdAsync(int accountId); Task> GetLocationsPagedAsync(int page, int pageSize, string? search = null); - Task> GetSiteOptionsAsync(string? search = null); + Task> GetSiteOptionsAsync( + string? search = null, + CancellationToken cancellationToken = default); Task CreateLocationAsync(CreateLocationDTO dto, string userId); Task UpdateLocationAsync(int id, UpdateLocationDTO dto, string userId); Task DeleteLocationAsync(int id, string userId); diff --git a/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs b/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs index 067234e..820fde2 100644 --- a/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs +++ b/SeaHavenIndustries.Tests/WorkOrderAccountScopeTests.cs @@ -816,8 +816,10 @@ public class WorkOrderAccountScopeTests => _inner.GetPagedAsync(page, pageSize, search); public Task<(IEnumerable Items, int TotalCount)> GetAddressbookPagedAsync(int page, int pageSize, string? search = null) => _inner.GetAddressbookPagedAsync(page, pageSize, search); - public Task> GetSiteOptionsAsync(string? search = null) - => _inner.GetSiteOptionsAsync(search); + public Task> GetSiteOptionsAsync( + string? search = null, + CancellationToken cancellationToken = default) + => _inner.GetSiteOptionsAsync(search, cancellationToken); public Task<(bool Exists, int? AccountId)> GetAccountScopeAsync( int locationId,