Skip to content

feat(controls): add 'kosli update control' command#988

Merged
pbeckham merged 2 commits into
mainfrom
6096-add-cli-command-for-control-update
Jul 2, 2026
Merged

feat(controls): add 'kosli update control' command#988
pbeckham merged 2 commits into
mainfrom
6096-add-cli-command-for-control-update

Conversation

@pbeckham

@pbeckham pbeckham commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What

Adds kosli update control CONTROL-IDENTIFIER — the missing update of CRUD for controls, wrapping the released PUT /api/v2/controls/{org}/{identifier} endpoint (body ControlPutInput). Follows on from the controls CLI in #986.

Flags:

  • --name / -n
  • --description / -d
  • --link (repeatable, name=url)

How it matches the API semantics

ControlPutInput has all-optional fields and the server merges based on model_fields_set — only keys present in the JSON body are touched. So:

  • The payload is built as an untyped map[string]interface{} and a key is added only when the user set that flag (cmd.Flags().Changed(...)). Omitted flags leave that field unchanged; --link replaces the whole links map, matching the server.
  • No-flags case: RequireAtLeastOneOfFlags gives a clear client-side error before hitting the API.
  • Not-found / validation errors surface verbatim from the API, consistent with get/archive control.
  • Beta-gated (betaCLIAnnotation), consistent with the other control commands.
  • Untyped payload (like update service-account), so the OpenAPI contract drift test needs no new entry.

Testing

  • make test_integration_single TARGET=TestUpdateControlCommandTestSuite — green. Cases: arg validation, no-flags error, name/description/links/multi-field success, not-found error.
  • make lint — 0 issues; make vet — clean.
  • kosli update control --help renders the beta notice and flags correctly.

Closes kosli-dev/server#6096

🤖 Generated with Claude Code

Adds the missing "update" of CRUD for controls, wrapping the released
PUT /api/v2/controls/{org}/{identifier} endpoint (body ControlPutInput).

- --name/-n, --description/-d, and repeatable --link (name=url)
- only flags the user set are sent, so omitted fields are left untouched
  (matches the server's model_fields_set merge); --link replaces all links
- RequireAtLeastOneOfFlags gives an actionable error when no updatable
  flag is provided; not-found and validation errors surface verbatim
- beta-gated (betaCLIAnnotation), consistent with the other control commands

Closes #6096

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @pbeckham's task in 1m 56s —— View job


PR Review — feat(controls): add 'kosli update control' command

  • Gather context (read changed files)
  • Static analysis (go build, go vet, go mod tidy)
  • Code review
  • Post review feedback

Verdict: LGTM. Clean, well-scoped slice that completes CRUD for controls and matches the established conventions in this repo.

Static analysis

Check Result
go build ./... ✅ clean
go vet ./cmd/kosli/ ✅ clean
go mod tidy + git diff go.mod go.sum ✅ no changes

What's done well

  • Merge semantics are correct. Building the payload as map[string]interface{} and gating each key on cmd.Flags().Changed(...) (updateControl.go:78-87) is exactly right for the server's model_fields_set merge — omitted flags leave fields untouched, and --link replaces the whole links map as documented.
  • The prior review comment is addressed. The dry-run test at updateControl_test.go:59-66 now pins the core behaviour — a name-only update must serialize to exactly {"name": ...} with no stray description/links. This guards the regression that plain success-message assertions would miss. 👍
  • Consistent with the codebase — beta annotation, RequireGlobalFlags in PreRunE, addDryRunFlag, untyped payload (like update service-account, so no OpenAPI drift-test entry needed), and good long-desc/example help text.
  • Good test coverage — arg validation, no-flags client-side error, single/multi-field success, not-found error, and the dry-run payload assertion.

Minor observations (non-blocking, no action required)

  • No way to clear all links. StringToStringVar can't represent "set links to empty", so a user can replace links but not remove them entirely via this command. Fine if the API/product doesn't need it; worth a mention only if clearing is a real use case.
  • url shadows the net/url import (updateControl.go:70) — harmless and consistent with createControl.go, so no change warranted.

Nice work — this is ready to merge as far as I can see.

Comment thread cmd/kosli/updateControl_test.go
Adds a dry-run case pinning the command's core behaviour: a name-only
update must send exactly {"name": ...} in the PUT body, with no stray
empty description or links. Guards the model_fields_set merge against
regressions the success-message assertions couldn't catch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pbeckham

pbeckham commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review. Triage of the remaining (non-blocking) points — all skipped, with reasoning:

  • Suggestion 1 — assert selective payload: ✅ fixed in 9173a5c (see the resolved inline thread).
  • Suggestion 2 — no way to clear all links: skipping. --link uses StringToStringVar, which requires name=url pairs, so "empty links" is inexpressible. Issue #6096 only specifies --link name=url to set links; clearing all links isn't a listed behaviour. Left as a known limitation — happy to add a dedicated affordance (e.g. --clear-links) in a follow-up if product wants it.
  • Suggestion 3 — --name "" edge case: skipping (working as intended). An empty --name is Changed, so it sends "name": "" and the server rejects it with name cannot be empty. That is exactly the "surface API errors verbatim" convention the rest of the command follows.
  • Nits (unused out param, url shadowing): skipping. Both are identical to createControl.go and every sibling control command — deliberate repo pattern, not defects introduced here.

@pbeckham pbeckham merged commit 45e42e6 into main Jul 2, 2026
20 checks passed
@pbeckham pbeckham deleted the 6096-add-cli-command-for-control-update branch July 2, 2026 08:06
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