Skip to content

feat: add product commands and a gh-style api command - #24

Draft
alukach wants to merge 2 commits into
mainfrom
feat/product-commands
Draft

alukach wants to merge 2 commits into
mainfrom
feat/product-commands

Conversation

@alukach

@alukach alukach commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Part of #21, stacked on the login-session PR. Builds on the product endpoints in source-cooperative/source.coop#651.

source-coop product list | view | create | edit | delete over /api/v1/products. As #21 asks, the CLI checks nothing itself and shows the API's errors field by field.

  • Auth: requests carry the login session's access token, or SOURCE_TOKEN if set. Without either they run signed out, which is enough to read public products, and a 401 says to run source-coop login.
  • Reading: list follows paging up to -L (default 30). Reads take --json, and view --web opens the product page.
  • Input: create and edit take fields as flags, from a JSON object with -F/--from-file (- for stdin), or both, with flags winning.
  • Prompts: when stdin and stderr are terminals (and SOURCE_PROMPT_DISABLED is unset), the CLI asks for whatever is still missing:
    • create: a title defaulting to one made from the ID, a data connection from those the account can use, and only the visibilities that connection allows.
    • Descriptions: typed on one line, or written in $VISUAL/$EDITOR by answering e, as in gh.
    • edit with no flags: asks which fields to change, and starts each from its current value.
    • delete: asks whether to keep the data, then for the product name typed back. Without a terminal it needs --yes.
  • Asking again: when the API rejects fields a person can answer (product ID, title, description, visibility, data connection), the CLI shows the API's message and asks for just those fields, keeping every other answer, then resends. Any other rejection is an error, as before.
  • Output: list prints a header and aligned columns on a terminal, and tab-separated rows without a header when piped. create and edit print the product URL on stdout and their message on stderr.
  • Errors: a request that gets no response says why (DNS, TLS, refused connection), not just which URL.

source-coop api PATH sends any /api/v1 request as the signed-in user, for whatever the other commands don't cover yet:

  • gh-style flags: -X for the method, -f key=value for a string, -F key=value for a JSON boolean, null or number, and --input FILE.
  • Fields go in the query string for GET and a JSON body otherwise, and the method defaults to POST when fields are given.
  • It prints the response and exits non-zero on an error status.

Follow-ups, not in this PR:

  • --jq.
  • Generating argument help and types from the OpenAPI document.
  • An endpoint listing the data connections an account may use for a product. The picker currently filters the legacy /data-connections route itself, and doesn't know about connections that require an account flag.

Deploying: needs the login-session PR, and source.coop#651 deployed. See the runbook in {{CLI_LOGIN_PR}}, steps 4 and 6.

Testing

  • cargo fmt --check, cargo clippy --all-targets -- -D warnings and cargo test (58 passed) all pass.
  • Product and api tests run against wiremock, using the request shapes from #651's routes. They include asking again for just the rejected field, a rejection of another field staying an error, the field picker, the piped table format, and api's query and body handling.
  • The binary, run against a local stand-in API:
    • product list and api sent the session's refreshed access token.
    • api exited 1 on a 404.
    • An unreachable host reported "Connection refused".
  • Not run against a live deployment: #651 isn't deployed yet.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_017poyHYEyxWEJct9X8Q71qF

@alukach alukach changed the title … feat: add product commands and a gh-style api command Oct 9, 2026
@alukach
alukach added this pull request to stack #25 October 9, 2026 19:20
Base automatically changed from feat/login-session to main October 9, 2026 19:38
alukach and others added 2 commits October 9, 2026 12:38
Adds `source-coop product`, the first gh-style command group from #21, over the `/api/v1/products` endpoints from source-cooperative/source.coop#651. A small API client sends the request and turns the API's `{"error": {code, message, field_errors}}` body into a message; the CLI does no validation of its own. Requests carry the login session's access token (or `SOURCE_TOKEN`, if set), and run signed out without one, which is enough to read public products.

`create` and `edit` take fields from flags, from a JSON file (`--from-file`), or both. When stdin and stderr are terminals, whatever is still missing is prompted for with defaults: a title from the product ID, the data connections the account can use, and the visibilities the chosen one allows. `edit` with no flags prompts from the product's current values and sends only what changed. `delete` asks whether to keep the data and for the name to be typed back; without a terminal it needs `--yes`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017poyHYEyxWEJct9X8Q71qF
When the API rejects a field and someone is at the terminal, the CLI shows why and asks for just that field again, keeping every other answer, then resends; the API stays the only judge. A rejection naming a field nobody can be asked for is an error, as before.

Descriptions can be written in `$VISUAL`/`$EDITOR`, gh's way: Enter keeps, `e` opens the editor, anything else is the text. `edit` with no flags first asks which fields to change.

`list` prints a header and aligned columns on a terminal, and tab-separated rows when piped. `create` and `edit` print the product URL on stdout and their message on stderr. A request that gets no response now says why (DNS, TLS, refused connection), not just which URL.

`source-coop api PATH` sends any `/api/v1` request as the signed-in user, with gh-style `-X`, `-f`, `-F` and `--input`, prints the response, and exits non-zero on an error status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017poyHYEyxWEJct9X8Q71qF
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