diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Repositories/ICentralUiRepository.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Repositories/ICentralUiRepository.cs index 5c73de68..3d1d0c27 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Repositories/ICentralUiRepository.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Interfaces/Repositories/ICentralUiRepository.cs @@ -32,6 +32,18 @@ public interface ICentralUiRepository /// Cancellation token. /// A task that resolves to a read-only list of matching instances. Task> GetInstancesFilteredAsync(int? siteId = null, int? templateId = null, string? searchTerm = null, CancellationToken cancellationToken = default); + /// + /// Returns one page of instances with SQL-side filtering, ordered by unique name, plus the total + /// number of rows matching the filter (before paging). + /// + /// Optional site IDs to restrict to; null means all sites, an empty collection matches nothing. + /// Optional template ID to filter by. + /// Optional unique-name prefix (StartsWith). + /// Rows to skip. + /// Maximum rows to return. + /// Cancellation token. + /// A task that resolves to the page of rows and the total matching count. + Task<(IReadOnlyList Rows, int Total)> GetInstancesPagedAsync(IReadOnlyCollection? siteIds, int? templateId, string? search, int skip, int take, CancellationToken cancellationToken = default); /// Returns the most recent deployment records up to the specified count. /// Maximum number of records to return. /// Cancellation token. diff --git a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Repositories/CentralUiRepository.cs b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Repositories/CentralUiRepository.cs index 02ddd0ed..2ddff48c 100644 --- a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Repositories/CentralUiRepository.cs +++ b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Repositories/CentralUiRepository.cs @@ -86,6 +86,38 @@ public class CentralUiRepository : ICentralUiRepository .ToListAsync(cancellationToken); } + /// + public async Task<(IReadOnlyList Rows, int Total)> GetInstancesPagedAsync( + IReadOnlyCollection? siteIds, + int? templateId, + string? search, + int skip, + int take, + CancellationToken cancellationToken = default) + { + var query = _context.Instances.AsNoTracking().AsQueryable(); + + if (siteIds is not null) + { + var ids = siteIds.ToList(); + query = query.Where(i => ids.Contains(i.SiteId)); + } + + if (templateId.HasValue) + query = query.Where(i => i.TemplateId == templateId.Value); + + if (!string.IsNullOrWhiteSpace(search)) + query = query.Where(i => i.UniqueName.StartsWith(search)); + + var total = await query.CountAsync(cancellationToken); + var rows = await query + .OrderBy(i => i.UniqueName) + .Skip(Math.Max(0, skip)) + .Take(Math.Max(0, take)) + .ToListAsync(cancellationToken); + return (rows, total); + } + /// public async Task> GetRecentDeploymentsAsync(int count, CancellationToken cancellationToken = default) { diff --git a/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryTests.cs b/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryTests.cs index 9939f08c..44888f79 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryTests.cs @@ -280,6 +280,38 @@ public class CentralUiRepositoryTests : IDisposable Assert.Single(instances); } + /// Verifies paged instances filter in SQL, order by name, page and report the total. + /// A task that represents the asynchronous operation. + [Fact] + public async Task GetInstancesPaged_FiltersOrdersAndPages() + { + var site1 = new Site("Site1", "S-001"); + var site2 = new Site("Site2", "S-002"); + var template = new Template("T1"); + _context.Sites.AddRange(site1, site2); + _context.Templates.Add(template); + await _context.SaveChangesAsync(); + + _context.Instances.AddRange( + new Instance("Pump3") { SiteId = site1.Id, TemplateId = template.Id }, + new Instance("Pump1") { SiteId = site1.Id, TemplateId = template.Id }, + new Instance("Pump2") { SiteId = site2.Id, TemplateId = template.Id }, + new Instance("XPump") { SiteId = site1.Id, TemplateId = template.Id }); + await _context.SaveChangesAsync(); + + var (rows, total) = await _repository.GetInstancesPagedAsync(null, null, "Pump", 1, 2); + Assert.Equal(3, total); + Assert.Equal(new[] { "Pump2", "Pump3" }, rows.Select(r => r.UniqueName)); + + var (scoped, scopedTotal) = await _repository.GetInstancesPagedAsync(new[] { site1.Id }, template.Id, "Pump", 0, 10); + Assert.Equal(2, scopedTotal); + Assert.Equal(new[] { "Pump1", "Pump3" }, scoped.Select(r => r.UniqueName)); + + var (none, noneTotal) = await _repository.GetInstancesPagedAsync(Array.Empty(), null, null, 0, 10); + Assert.Empty(none); + Assert.Equal(0, noneTotal); + } + /// Verifies that recent deployments are returned in reverse chronological order. /// A task that represents the asynchronous operation. [Fact]