fix(cli): single-key note rows render as raw indented lines under their step (Task 14 review)
This commit is contained in:
@@ -255,6 +255,20 @@ internal static class CommandHelpers
|
||||
// element, in first-seen order. Using only items[0] would silently drop
|
||||
// columns for any later element with a different shape.
|
||||
var objectItems = items.Where(i => i.ValueKind == JsonValueKind.Object).ToList();
|
||||
|
||||
// A "note" property that appears ONLY on single-property rows (a trailing
|
||||
// run-level note, or — Task 14 review, 2026-09-09 — a step's folded log/exception
|
||||
// line) is not a real column: every other row's cell there would render blank,
|
||||
// and column padding would swallow the raw line's own leading indentation. Such
|
||||
// rows print as a raw unpadded line instead, at their position in the sequence,
|
||||
// and "note" is excluded from the header union so nothing renders a blank column
|
||||
// for it.
|
||||
var noteOnlyRows = objectItems
|
||||
.Where(i => i.EnumerateObject().Any(p => p.Name == "note"))
|
||||
.ToList();
|
||||
var noteIsRawLine = noteOnlyRows.Count > 0
|
||||
&& noteOnlyRows.All(i => i.EnumerateObject().Count() == 1);
|
||||
|
||||
string[] headers;
|
||||
if (objectItems.Count > 0)
|
||||
{
|
||||
@@ -262,8 +276,12 @@ internal static class CommandHelpers
|
||||
var known = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var item in objectItems)
|
||||
foreach (var prop in item.EnumerateObject())
|
||||
{
|
||||
if (noteIsRawLine && prop.Name == "note")
|
||||
continue;
|
||||
if (known.Add(prop.Name))
|
||||
seen.Add(prop.Name);
|
||||
}
|
||||
headers = seen.ToArray();
|
||||
}
|
||||
else
|
||||
@@ -275,12 +293,20 @@ internal static class CommandHelpers
|
||||
{
|
||||
if (item.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
return headers.Select(h =>
|
||||
if (noteIsRawLine
|
||||
&& item.EnumerateObject().Count() == 1
|
||||
&& item.TryGetProperty("note", out var noteValue))
|
||||
{
|
||||
return OutputFormatter.TableRow.Raw(
|
||||
noteValue.ValueKind == JsonValueKind.Null ? "" : noteValue.ToString());
|
||||
}
|
||||
|
||||
return OutputFormatter.TableRow.Columns(headers.Select(h =>
|
||||
item.TryGetProperty(h, out var val)
|
||||
? val.ValueKind == JsonValueKind.Null ? "" : val.ToString()
|
||||
: "").ToArray();
|
||||
: "").ToArray());
|
||||
}
|
||||
return new[] { item.ToString() };
|
||||
return OutputFormatter.TableRow.Columns(new[] { item.ToString() });
|
||||
});
|
||||
|
||||
OutputFormatter.WriteTable(rows, headers);
|
||||
|
||||
@@ -51,7 +51,7 @@ public static class FlowDebugCommands
|
||||
+ "(default 5 min), so a larger value still 504s at that ceiling",
|
||||
DefaultValueFactory = _ => 120,
|
||||
};
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row" };
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row (table output only)" };
|
||||
var cmd = new Command("run-method") { Description = "Run an inbound API method on the active central node with full tracing and print the flow's steps" };
|
||||
cmd.Add(methodIdOption);
|
||||
cmd.Add(paramsOption);
|
||||
@@ -105,7 +105,7 @@ public static class FlowDebugCommands
|
||||
+ "(default 5 min), so a larger value still 504s at that ceiling",
|
||||
DefaultValueFactory = _ => 120,
|
||||
};
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row" };
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row (table output only)" };
|
||||
var cmd = new Command("run-script") { Description = "Run one site script with tracing — a ValueChange/Conditional script is fired by writing its trigger attribute (--value); any other script is called directly (--params)" };
|
||||
cmd.Add(instanceIdOption);
|
||||
cmd.Add(scriptOption);
|
||||
@@ -162,7 +162,7 @@ public static class FlowDebugCommands
|
||||
var attributeOption = new Option<string>("--attribute") { Description = "Canonical attribute name to write", Required = true };
|
||||
var valueOption = new Option<string>("--value") { Description = "Value to write (canonical string form)", Required = true };
|
||||
var waitOption = new Option<int>("--wait-seconds") { Description = "How long to wait for a triggered script run before reporting NO_TRIGGER_WITHIN_WINDOW", DefaultValueFactory = _ => 45 };
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row" };
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row (table output only)" };
|
||||
var cmd = new Command("run-value-change") { Description = "Write an attribute through the traced path and capture the script run(s) the change triggers — the attribute-level path (also fires Expression triggers); prefer run-script to run a named script" };
|
||||
cmd.Add(instanceIdOption);
|
||||
cmd.Add(attributeOption);
|
||||
@@ -195,7 +195,7 @@ public static class FlowDebugCommands
|
||||
private static Command BuildReplay(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var executionIdOption = new Option<Guid>("--execution-id") { Description = "Any execution ID in the chain (from an audit row)", Required = true };
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row" };
|
||||
var showLogsOption = new Option<bool>("--show-logs") { Description = "Print each step's ScriptLog lines and exception detail as indented lines under its row (table output only)" };
|
||||
var cmd = new Command("replay") { Description = "Replay a past flow from the central audit store" };
|
||||
cmd.Add(executionIdOption);
|
||||
cmd.Add(showLogsOption);
|
||||
|
||||
@@ -18,13 +18,18 @@ namespace ZB.MOM.WW.ScadaBridge.CLI.Commands;
|
||||
/// to the generic renderer unprojected, the object takes the Property/Value branch and the
|
||||
/// whole event array lands in one cell as raw JSON. This projects the events through the same
|
||||
/// <see cref="FlowGraphBuilder"/> the Flow Debugger page uses and prints one row per STEP —
|
||||
/// <c>occurred | kind | lane | target | durationMs | status | lifecycle | correlation | error</c> — in the
|
||||
/// order the page draws them, so a terminal user can read the swimlane top-to-bottom. An
|
||||
/// <c>occurred | kind | lane | target | durationMs | status | lifecycle | correlation | error | logs</c> —
|
||||
/// in the order the page draws them, so a terminal user can read the swimlane top-to-bottom. An
|
||||
/// outbound operation's rows (a cached call's Submit → Forwarded → Attempted → Resolve, a
|
||||
/// notification's Send → Deliver) fold into ONE row whose <c>lifecycle</c> column counts them
|
||||
/// and whose <c>status</c> is the latest row's (outbound-calls plan 2026-09-06, D3). A trailing
|
||||
/// <c>note</c> row reports a failed / incomplete run (with the error code verbatim, e.g.
|
||||
/// <c>STANDBY_NODE</c>) or a truncated event list.
|
||||
/// and whose <c>status</c> is the latest row's (outbound-calls plan 2026-09-06, D3). <c>logs</c>
|
||||
/// counts the step's attached <c>ScriptLog</c> lines (step-logs design 2026-09-09); passing
|
||||
/// <c>showLogs</c> additionally prints those lines, and a failed step's exception detail, as
|
||||
/// raw indented lines immediately under that step's row — <see cref="CommandHelpers"/>'s table
|
||||
/// renderer special-cases a single-property <c>{ "note": … }</c> row to print it that way
|
||||
/// instead of padding it into a table cell. A trailing <c>note</c> row (the same special case)
|
||||
/// reports a failed / incomplete run (with the error code verbatim, e.g. <c>STANDBY_NODE</c>)
|
||||
/// or a truncated event list.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>JSON output is never touched — <c>--format json</c> gets the full raw result.</para>
|
||||
@@ -179,7 +184,7 @@ internal static class FlowDebugTableProjection
|
||||
/// <summary>Formats one <see cref="FlowLogLine"/> as <c> HH:mm:ss.fff LEVEL source message</c> (UTC, level upper-cased and padded to 5 chars).</summary>
|
||||
private static string FormatLogLine(FlowLogLine line)
|
||||
{
|
||||
var time = line.OccurredAtUtc.UtcDateTime.ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture);
|
||||
var time = line.OccurredAtUtc.ToUniversalTime().ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture);
|
||||
var level = (line.Level ?? "info").ToUpperInvariant().PadRight(5);
|
||||
return $" {time} {level} {line.Source} {line.Message}";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user