Skip to content

fix(cli): show nested fields in single-object table output - #193

Open
ysyneu wants to merge 2 commits into
mainfrom
bugfix/table-output-nested-fields
Open

ysyneu wants to merge 2 commits into
mainfrom
bugfix/table-output-nested-fields

Conversation

@ysyneu

@ysyneu ysyneu commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Problem

When a command returns a single object, the default table output listed only the object's top-level scalar fields. Arrays, nested objects and maps were skipped with no sign that anything was missing. --json and --output-format toon were not affected.

For example, member notify --dry-run prints {html, recipients: [...]}, but the table showed only html, and the multi-line email body spilled onto lines that looked like extra rows:

FIELD  VALUE
HTML   <html>
  <body style="font-family: Georgia">
    <h1>Daily report</h1>
    <p>Al...

Every command that renders through the shared generic renderer (renderGenericTable) had the same gap. That covers all generated commands plus the curated automation, monit query and Prometheus label-values commands. Any detail response whose content is nested lost that content in the table, such as PrometheusLabelValuesResponse.data or KnowledgeGetResponse.files.

Change

  • Single-object tables list every non-empty value. The FIELD/VALUE table walks the whole object. Each nested value is named by its path: struct fields use the existing upper-cased names, map entries use their key (sorted), and array elements use their index. List pages keep their column tables.
  • Table cells stay on one line. TablePrinter collapses line breaks and indentation inside a value to single spaces, so a multi-line value stays on its own row. Long values are still cut at the column width with ... unless --no-trunc is set. For a large html body the default view shows a readable one-line preview, and --no-trunc or --json returns the whole body.
  • A bare array of scalars no longer panics in table mode. Before this change, monit datasource-sls-logstores crashed with reflect: NumField of non-struct type string. It now prints JSON, like other shapes a table cannot model.

After:

FIELD                    VALUE
HTML                     <html> <body style="font-family: Georgia"> <h1>Daily report</h1> <p>All servi...
RECIPIENTS[0].PERSON_ID  5068740052131
RECIPIENTS[0].STATUS     accepted
RECIPIENTS[1].PERSON_ID  5068740052132
RECIPIENTS[1].REASON     no_email
RECIPIENTS[1].STATUS     skipped

Alternatives considered

  • Render arrays of objects as labeled sub-tables using the list-table columns. Rejected. Those columns are a projection: curated columns, an 8-column heuristic, and nested fields skipped. Fields nested inside array rows would still vanish without notice, for example route cases or escalation layers. Arrays of maps or of arrays cannot be laid out as columns at all.
  • Put nested values in a cell as compact JSON. Rejected. At the default cell width almost all of the content would be cut off, and timestamps would print as raw integers.
  • Print long strings as separate blocks after the table. Rejected. A 60 KB body would flood the terminal in the default view.

Tests

  • TestRenderGenericTable_DetailShowsNestedFields checks the exact output for a nested object, an array of objects, an array of scalars, a map, an any value and a nil pointer.
  • TestMemberNotifyDryRunTableShowsRecipients runs member notify --dry-run in the default table mode against the stub server. It checks every recipient outcome and that the multi-line body stays on one row.
  • TestRenderGenericTable_TopLevelScalarArray checks that a bare scalar array prints without a panic.
  • TestTablePrinter_MultilineValueStaysOnItsRow checks that a cell stays on one line with and without truncation.

All four failed before the fix. make test, make lint, make check-cards and make build pass.

The default table output of a single-object response listed only its
top-level scalar fields. Arrays, nested objects and maps were skipped
without any sign, so `member notify --dry-run` printed the email html but
none of the per-recipient outcomes, and a detail response whose useful
content is nested looked nearly empty.

The key/value table now lists every non-empty scalar value reachable from
the object, naming nested ones by their path (OWNER.EMAIL, LABELS.env,
RECIPIENTS[0].STATUS). List pages keep their column tables.

Table cells are also folded to one line. A multi-line value such as an
email body used to spill onto following lines that read as extra rows;
line breaks and indentation now collapse to single spaces, and long values
are still truncated unless --no-trunc is set.

A bare array of scalars (monit datasource-sls-logstores) no longer panics
in table mode; it prints as JSON like other shapes a table cannot model.
A []byte or json.RawMessage field reached the key/value walk as a slice
of uint8, so it would print one row per byte instead of its value.

Byte slices are now single table cells, shown the way --json encodes
them: a json.RawMessage as its compact JSON text, any other []byte as
base64.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant