feat(instances): GetInstancesPagedAsync SQL-side filter/order/page
This commit is contained in:
@@ -32,6 +32,18 @@ public interface ICentralUiRepository
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of matching instances.</returns>
|
||||
Task<IReadOnlyList<Instance>> GetInstancesFilteredAsync(int? siteId = null, int? templateId = null, string? searchTerm = null, CancellationToken cancellationToken = default);
|
||||
/// <summary>
|
||||
/// Returns one page of instances with SQL-side filtering, ordered by unique name, plus the total
|
||||
/// number of rows matching the filter (before paging).
|
||||
/// </summary>
|
||||
/// <param name="siteIds">Optional site IDs to restrict to; null means all sites, an empty collection matches nothing.</param>
|
||||
/// <param name="templateId">Optional template ID to filter by.</param>
|
||||
/// <param name="search">Optional unique-name prefix (StartsWith).</param>
|
||||
/// <param name="skip">Rows to skip.</param>
|
||||
/// <param name="take">Maximum rows to return.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the page of rows and the total matching count.</returns>
|
||||
Task<(IReadOnlyList<Instance> Rows, int Total)> GetInstancesPagedAsync(IReadOnlyCollection<int>? siteIds, int? templateId, string? search, int skip, int take, CancellationToken cancellationToken = default);
|
||||
/// <summary>Returns the most recent deployment records up to the specified count.</summary>
|
||||
/// <param name="count">Maximum number of records to return.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
|
||||
@@ -86,6 +86,38 @@ public class CentralUiRepository : ICentralUiRepository
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<(IReadOnlyList<Instance> Rows, int Total)> GetInstancesPagedAsync(
|
||||
IReadOnlyCollection<int>? 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<DeploymentRecord>> GetRecentDeploymentsAsync(int count, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
@@ -280,6 +280,38 @@ public class CentralUiRepositoryTests : IDisposable
|
||||
Assert.Single(instances);
|
||||
}
|
||||
|
||||
/// <summary>Verifies paged instances filter in SQL, order by name, page and report the total.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[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<int>(), null, null, 0, 10);
|
||||
Assert.Empty(none);
|
||||
Assert.Equal(0, noneTotal);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that recent deployments are returned in reverse chronological order.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user