Repository navigation
Conversation
Generated commands whose SDK method returns no typed data printed a
fixed acknowledgement assembled from their own HTTP verb and path, e.g.
"OK: POST /incident/ack", and discarded the response. That line restated
what the caller had just typed, so it said nothing about the outcome of
the write it was reporting, and in --json it arrived as a rendered
English sentence under a "message" key with no field to read.
All 118 such endpoints genuinely carry no payload: 110 mutations whose
200 envelope declares `data` as an empty object, and 8 CSV/file
downloads with no application/json 200 body at all, whose bytes already
stream through Response.Raw. So the fix is not to invent a payload but
to say there was none, and to hand back the request id the CLI had been
dropping on every write — the one fact of the call a caller cannot
derive from its own arguments, and what it has to quote when reporting
a write that went wrong.
$ flashduty incident ack inc-1
OK: the API returned no payload (request_id 01HK8XQE3Z7JM2NTFQ5YJ8P9R4).
$ flashduty incident ack inc-1 --json
{
"request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4"
}
One rule in the generator derives this from the SDK method's own shape,
so it reaches every generated command with no per-command list and no
flag. Three curated commands carried the same canned line and now call
the same helper: a curated command shadows its generated twin at
registration, so changing only the generator would have left the
command users actually reach unchanged.
The helper, printRawOrNoPayload, also absorbs the Response.Raw check
each call site used to repeat, which is what keeps the two in step: the
claim "no payload" is now made by the same code that establishes it, so
a caller cannot print it while holding bytes. mapping-data upload was
missing that check and would have made exactly that claim.
Output shape, for callers parsing it: `--json` and `--toon` now carry
request_id instead of message, and carry an empty object when the server
reports no id. The Response.Raw branch is untouched, so a CSV or file
download still streams verbatim and `> file.csv` is unaffected.
Tests pin the default line by equality rather than substring, because
callers read this through `head` and a line pushed down by output added
above it has to fail. A spec-driven test asserts the premise that every
one of these operations declares no payload, so an endpoint that later
grows a real response fails the build instead of being dropped in
silence.
The helper these cover is printRawOrNoPayload/printNoPayload, so test names and file names reading WriteAck pointed at an identifier that no longer exists. Renames only, plus one comment reflowed; no assertion changed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
118 generated commands printed a fixed acknowledgement assembled from their own HTTP verb and path and threw the API response away:
That line restates what the caller just typed. It says nothing about the outcome of the write it is reporting, so a caller could not tell from it what had happened. In
--jsonit was worse — a rendered English sentence under amessagekey, with no field to read:{"message": "OK: POST /incident/ack"}What these endpoints actually return
Checked against the OpenAPI spec in the linked
go-flashdutymodule, not guessed. All 118 fall into two classes, and neither returns an object or an id:datais an empty object —EmptyResponse,EmptyObject,PlatformEmptyObject,MemberEmptyObject,CalendarEmptyObject,ScheduleEmptyObject,RuleEmptyResponse,RumErrorIngestionEmptyResponse, or an inline{type: object}with no propertiesapplication/json200 body at all (application/octet-streamortext/csv)The download set is
insight/{incident,channel,responder,team}/export,rum/issue/export,status-page/subscriber/export,enrichment/mapping/data/download, andGET /safari/artifact/stream.So there is nothing to report that the server stored — and inventing one would be a lie. What the server does report, and the CLI was discarding on every single write, is the
request_id: perSuccessEnvelopeit is present on every 2xx, mirrored in theFlashcat-Request-Idheader, and the thing to quote when reporting a problem.New output
When the server reports no id, the line and the record both leave it out rather than printing a hole:
A download is unchanged — bytes reach stdout verbatim, so
> file.csvstill works:How
One rule in
internal/cmd/cligen, keyed off the SDK method's own shape (no data return, reflected at generation time) — no per-command list, no flag. It routes every such command to one runtime helper,printRawOrNoPayloadininternal/cli/gen_support.go.That helper also absorbs the
resp != nil && len(resp.Raw) > 0check each call site used to repeat. The SDK fillsResponse.Rawexactly when a 2xx body is not envelope JSON, so that check is the runtime discriminator between a download and an empty envelope — and folding it in is what keeps the claim and the response in step: "no payload" is now asserted by the same code that establishes it, so no caller can print it while holding bytes.enrichment mapping-data-uploadwas missing the check and would have made exactly that claim.Generated code net −472 lines (118 insertions, 590 deletions across 22 files).
Three curated commands carried the same canned line and now use the same helper (
alert.go,insight.go,upload.go). A curated command shadows its generated twin at registration, so changing only the generator would have left the command users actually reach unchanged.Breaking change for machine callers
--json/--toonon these commands now carryrequest_idinstead ofmessage. A script reading.messagefrom a write will need updating. Table output and exit codes are otherwise unaffected, and downloads are untouched.Verification
make(fmt, lint, test, build): lint 0 issues,go test -race ./...all packages pass.make check-cards:skilldoc: cards OK, unchanged.go run ./internal/cmd/cligenandmake gen-cardsre-run produce no further drift.head, so a line pushed down by output added above it must fail.internal/cmd/cligen/write_ack_test.goasserts the premise from the spec — that all 118 of these operations declare no payload — so an endpoint that later grows a real response fails the build instead of being dropped in silence.e2e/is compile-checked only (go vet -tags e2e ./e2e/). It was not run: it needsFLASHDUTY_E2E_APP_KEYand performs real writes. Everything above ran against local stub servers; nothing was written to a live account.Not done, deliberately
noise.md,template.md) is about verifying that content round-tripped byte for byte — which an empty envelope still cannot confirm. Removing it would claim something this change does not deliver.