Skip to content

format: stop leaking relationship IDs into JSON:API attributes in --json/--jq - #79

Merged
brandonc merged 1 commit into
hashicorp:mainfrom
jordanenglish:fix/jsonapi-relationship-mutation
Jul 14, 2026
Merged

format: stop leaking relationship IDs into JSON:API attributes in --json/--jq#79
brandonc merged 1 commit into
hashicorp:mainfrom
jordanenglish:fix/jsonapi-relationship-mutation

Conversation

@jordanenglish

Copy link
Copy Markdown
Contributor

Description

For tfctl api, one-to-one relationship IDs are pulled up into the flattened
row so they show in table and pretty output (e.g. an organization column).
resourceAsMap did this by writing the ID into the resource's attributes
map, but that map is aliased from the raw JSON:API payload that backs --json
and --jq output. As a result the pulled-up IDs leaked into attributes in
JSON output, keys the server never returned under attributes, corrupting
machine-readable output.

This writes the pulled-up relationship IDs into the per-display row instead of
mutating the shared attributes map, so table/pretty output is unchanged while
--json/--jq reflect the server payload exactly.

Example Output

Given a workspace with an organization relationship, before:

$ tfctl api /organizations/my-org/workspaces/my-ws --json | jq '.data.attributes | keys'
[
  "auto-apply",
  "name",
  "organization"
]

After:

$ tfctl api /organizations/my-org/workspaces/my-ws --json | jq '.data.attributes | keys'
[
  "auto-apply",
  "name"
]

PR Checklist

  • Run npx changie new or install changie to prepare a new changelog entry for the next set of release notes.
    • Added .changes/unreleased/BUG FIXES-*.yaml (kind: BUG FIXES).
  • Ensure any command changes are sensitive to these global flags:
    • --json — Force machine readable output to stdout. Does not apply to stderr.
    • --markdown — Force markdown output to stdout. Does not apply to stderr.
    • --dry-run — Don't make any actual writes or other mutations. Describe what would have changed to stderr.
    • --quiet — Don't render output to stdout.
    • This directly fixes --json/--jq output. Table/pretty (--markdown included) output is unchanged; a regression test confirms the existing pull-up display behavior still holds.
  • Get the logging interface from the context and add debug logging for interesting conditions and nonfatal situations.
    • No new nonfatal conditions are introduced.
  • Run make gen/screenshot if the root command output changes.
    • Not required: root command output is unchanged.
  • Add the Autocomplete field to positional arguments and flags to assist shell autocomplete.
    • N/A: no new arguments or flags.

PCI review checklist

  • I have documented a clear reason for, and description of, the change I am making.

  • If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.

    • Reverting the PR fully removes the change; no additional revert steps or data migration.
  • If applicable, I've documented the impact of any changes to security controls.

    • N/A: no security-control changes.

resourceAsMap pulled one-to-one relationship IDs up into the attributes
map for table and pretty display, but that map is shared with the raw
payload backing --json and --jq output. The pulled-up IDs then appeared
under "attributes" in JSON output, keys the server never returned there.

Write the pulled-up IDs into the per-display row instead of mutating the
shared attributes map. Adds tests asserting JSON output is unpolluted and
the raw payload is stable across table and JSON rendering.
@jordanenglish
jordanenglish marked this pull request as ready for review July 13, 2026 17:56
@brandonc
brandonc merged commit 341171c into hashicorp:main Jul 14, 2026
2 checks passed
@brandonc

Copy link
Copy Markdown
Collaborator

Thanks again!

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.

2 participants