feat(management): additive Skip/Take paging on template/instance lists (arch-review P2)
Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -212,19 +212,25 @@ public static class InstanceCommands
|
||||
var siteIdOption = new Option<int?>("--site-id") { Description = "Filter by site ID" };
|
||||
var templateIdOption = new Option<int?>("--template-id") { Description = "Filter by template ID" };
|
||||
var searchOption = new Option<string?>("--search") { Description = "Search term" };
|
||||
var skipOption = new Option<int>("--skip") { Description = "Offset paging: number of items to skip (default 0)" };
|
||||
var takeOption = new Option<int?>("--take") { Description = "Offset paging: max items to return (1..1000; omit for unlimited)" };
|
||||
|
||||
var cmd = new Command("list") { Description = "List instances" };
|
||||
cmd.Add(siteIdOption);
|
||||
cmd.Add(templateIdOption);
|
||||
cmd.Add(searchOption);
|
||||
cmd.Add(skipOption);
|
||||
cmd.Add(takeOption);
|
||||
cmd.SetAction(async (ParseResult result) =>
|
||||
{
|
||||
var siteId = result.GetValue(siteIdOption);
|
||||
var templateId = result.GetValue(templateIdOption);
|
||||
var search = result.GetValue(searchOption);
|
||||
var skip = result.GetValue(skipOption);
|
||||
var take = result.GetValue(takeOption);
|
||||
return await CommandHelpers.ExecuteCommandAsync(
|
||||
result, urlOption, formatOption, usernameOption, passwordOption,
|
||||
new ListInstancesCommand(siteId, templateId, search));
|
||||
new ListInstancesCommand(siteId, templateId, search, skip, take));
|
||||
});
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@@ -59,13 +59,19 @@ public static class TemplateCommands
|
||||
Description = "Include full template definitions (all attributes/alarms/scripts) in table output. "
|
||||
+ "Without it, table output is a compact summary (counts only). JSON output is always full."
|
||||
};
|
||||
var skipOption = new Option<int>("--skip") { Description = "Offset paging: number of items to skip (default 0)" };
|
||||
var takeOption = new Option<int?>("--take") { Description = "Offset paging: max items to return (1..1000; omit for unlimited)" };
|
||||
var cmd = new Command("list") { Description = "List all templates (compact table summary; use --detail for the full dump)" };
|
||||
cmd.Add(detailOption);
|
||||
cmd.Add(skipOption);
|
||||
cmd.Add(takeOption);
|
||||
cmd.SetAction(async (ParseResult result) =>
|
||||
{
|
||||
var detail = result.GetValue(detailOption);
|
||||
var skip = result.GetValue(skipOption);
|
||||
var take = result.GetValue(takeOption);
|
||||
return await CommandHelpers.ExecuteCommandAsync(
|
||||
result, urlOption, formatOption, usernameOption, passwordOption, new ListTemplatesCommand(),
|
||||
result, urlOption, formatOption, usernameOption, passwordOption, new ListTemplatesCommand(skip, take),
|
||||
tableProjector: detail ? null : TemplateTableProjection.ProjectSummary);
|
||||
});
|
||||
return cmd;
|
||||
|
||||
@@ -101,6 +101,8 @@ scadabridge --url <url> --format json template list # full JSON (always)
|
||||
| Option | Required | Description |
|
||||
|--------|----------|-------------|
|
||||
| `--detail` | no | Include full definitions in table output (no effect on JSON) |
|
||||
| `--skip` | no | Offset paging: number of items to skip (default 0) |
|
||||
| `--take` | no | Offset paging: max items to return (clamped 1..1000; omit for unlimited) |
|
||||
|
||||
#### `template get`
|
||||
|
||||
@@ -450,7 +452,7 @@ scadabridge --url <url> instance get --id <int>
|
||||
List instances, with optional filters.
|
||||
|
||||
```sh
|
||||
scadabridge --url <url> instance list [--site-id <int>] [--template-id <int>] [--search <string>]
|
||||
scadabridge --url <url> instance list [--site-id <int>] [--template-id <int>] [--search <string>] [--skip <int>] [--take <int>]
|
||||
```
|
||||
|
||||
| Option | Required | Description |
|
||||
@@ -458,6 +460,8 @@ scadabridge --url <url> instance list [--site-id <int>] [--template-id <int>] [-
|
||||
| `--site-id` | no | Filter by site ID |
|
||||
| `--template-id` | no | Filter by template ID |
|
||||
| `--search` | no | Search term matched against instance name |
|
||||
| `--skip` | no | Offset paging: number of items to skip (default 0). Applied after site-scope filtering. |
|
||||
| `--take` | no | Offset paging: max items to return (clamped 1..1000; omit for unlimited) |
|
||||
|
||||
#### `instance create`
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
|
||||
public record ListInstancesCommand(int? SiteId = null, int? TemplateId = null, string? SearchTerm = null);
|
||||
// Skip/Take are additive offset paging (arch-review P2), applied AFTER in-memory
|
||||
// site-scope filtering. Take = null preserves the historical unpaged behaviour.
|
||||
public record ListInstancesCommand(int? SiteId = null, int? TemplateId = null, string? SearchTerm = null, int Skip = 0, int? Take = null);
|
||||
public record GetInstanceCommand(int InstanceId);
|
||||
public record CreateInstanceCommand(string UniqueName, int TemplateId, int SiteId, int? AreaId = null);
|
||||
public record MgmtDeployInstanceCommand(int InstanceId);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
|
||||
public record ListTemplatesCommand;
|
||||
// Skip/Take are additive offset paging (arch-review P2). Take = null preserves the
|
||||
// historical unpaged behaviour (unlimited), so existing callers/UI are untouched.
|
||||
public record ListTemplatesCommand(int Skip = 0, int? Take = null);
|
||||
public record GetTemplateCommand(int TemplateId);
|
||||
public record CreateTemplateCommand(string Name, string? Description, int? ParentTemplateId);
|
||||
|
||||
|
||||
@@ -295,7 +295,7 @@ public class ManagementActor : ReceiveActor
|
||||
return command switch
|
||||
{
|
||||
// Templates
|
||||
ListTemplatesCommand => await HandleListTemplates(sp),
|
||||
ListTemplatesCommand cmd => await HandleListTemplates(sp, cmd),
|
||||
GetTemplateCommand cmd => await HandleGetTemplate(sp, cmd),
|
||||
CreateTemplateCommand cmd => await HandleCreateTemplate(sp, cmd, user.Username),
|
||||
UpdateTemplateCommand cmd => await HandleUpdateTemplate(sp, cmd, user.Username),
|
||||
@@ -559,12 +559,22 @@ public class ManagementActor : ReceiveActor
|
||||
// Template handlers
|
||||
// ========================================================================
|
||||
|
||||
private static async Task<object?> HandleListTemplates(IServiceProvider sp)
|
||||
private static async Task<object?> HandleListTemplates(IServiceProvider sp, ListTemplatesCommand cmd)
|
||||
{
|
||||
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
|
||||
return await repo.GetAllTemplatesAsync();
|
||||
var templates = await repo.GetAllTemplatesAsync();
|
||||
return Page(templates, cmd.Skip, cmd.Take);
|
||||
}
|
||||
|
||||
// Additive offset paging (arch-review P2). Take = null (or non-positive) keeps
|
||||
// today's unlimited behaviour; Take is clamped to 1..1000; Skip floors at 0.
|
||||
// Applied AFTER any in-memory scope filtering by the caller.
|
||||
private static List<T> Page<T>(IEnumerable<T> source, int skip, int? take) =>
|
||||
source
|
||||
.Skip(Math.Max(0, skip))
|
||||
.Take(take is > 0 and <= 1000 ? take.Value : int.MaxValue)
|
||||
.ToList();
|
||||
|
||||
private static async Task<object?> HandleGetTemplate(IServiceProvider sp, GetTemplateCommand cmd)
|
||||
{
|
||||
var repo = sp.GetRequiredService<ITemplateEngineRepository>();
|
||||
@@ -780,7 +790,9 @@ public class ManagementActor : ReceiveActor
|
||||
var permittedIds = new HashSet<string>(user.PermittedSiteIds);
|
||||
instances = instances.Where(i => permittedIds.Contains(i.SiteId.ToString())).ToList();
|
||||
}
|
||||
return instances;
|
||||
// Paging is applied AFTER the site-scope filter so a scoped user never sees
|
||||
// an out-of-scope instance surface into their page window.
|
||||
return Page(instances, cmd.Skip, cmd.Take);
|
||||
}
|
||||
|
||||
private static async Task<object?> HandleGetInstance(IServiceProvider sp, GetInstanceCommand cmd, AuthenticatedUser user)
|
||||
|
||||
Reference in New Issue
Block a user