Repository navigation
refactor: decode assert artifact, get artifact and get policy responses into typed structs - #1246
Conversation
The table printer walked five levels of map[string]any with unchecked type assertions, so a response missing any expected field panicked the CLI. Typed structs make a missing field a zero value instead, document the response shape in one place, and let the failure-message loop become a standalone helper.
The table printer had fifteen unchecked type assertions, and a response without running, exited or history lists panicked. Timestamps are json.Number because the artifacts API returns created_at as a numeric string while other endpoints return numbers; formattedTimestamp learns to treat an empty json.Number as a missing value.
The table printer indexed versions[len-1] without a length check, so a policy with no versions panicked. It now returns an error.
|
Claude finished @dangrondahl's task in 1m 12s —— View job Follow-up review completeNew commit Previous findings touched by this commit:
Previous findings not touched by this commit (already addressed in New issues in the touched lines: none found. The change is a minimal, well-targeted fix with direct test coverage. |
Move-only. The rest of cmd/kosli declares types before newXCmd, and assertArtifactResult is used by run before its old declaration.
- get artifact prints the Trail and Name in template rows whenever the server sends the key, even with an empty value, as main does - formattedTimestamp parses a json.Number through the string case instead of a second copy of the parse - the assert printer tests restore the package logger they replace
…lone The compliance check decoded the whole response into the table printer's types, so a type change in any nested field turned a compliant -o json run into a decode error and a failing exit code.
The table printers for
assert artifact,get artifactandget policynow decode the API response into small typed structs. Until now they decoded intomap[string]anyand read fields through unchecked type assertions. With an assertion, a missing or renamed field panics the CLI. With a struct, it becomes a zero value, or a returned error where the field is genuinely required.Each struct is declared next to its command's options, like the other types in
cmd/kosli. It declares only the fields the printer reads, and timestamps arejson.Number.-o jsonoutput is untouched: it still prints the raw server body.assert artifactassertArtifactResultwith nested policy evaluations, rule evaluations and flows. The failure-message loop is now its own function,policyFailures.get artifactartifactResponsewithrunning,exitedandhistory.formattedTimestampnow accepts ajson.Number, because the artifacts API sendscreated_atas a numeric string. An empty one counts as missing, and anything else is parsed by the existing string case.trail_nameandtemplate_reference_nameare*string, so their rows still print whenever the server sends the key, even with an empty value, as onmain.get policypolicyResponse. A policy with no versions now returnspolicy '<name>' has no versionsinstead of panicking with index out of range.These commits were written before #1206 and are rebased onto main here. This is groundwork for the typed listing renderer for the
listcommands, which will follow the same pattern.Testing
json.Number, trail rows sent with empty values, and a policy with no versions.mainand this branch and diffed their output against a stub server serving recorded fixtures, in both table and JSON format:get artifactwithtrail_nameandtemplate_reference_namesent as""get policywithversions: []get artifactwithoutrunning,exited,historyor URLsassert artifactwith only{"compliant": true}COMPLIANT.-o jsonfor every fixtureChecklist
charts/k8s-reporter/) updated, if needed. Note: these changes live in a separate PR (n/a)