Skip to content

feat(cli): reach the four inbox routes the generic factory cannot - #28

Draft
yvoolab wants to merge 1 commit into
dualentry:mainfrom
yvoolab:inbox-typed-subresources
Draft

feat(cli): reach the four inbox routes the generic factory cannot#28
yvoolab wants to merge 1 commit into
dualentry:mainfrom
yvoolab:inbox-typed-subresources

Conversation

@yvoolab

@yvoolab yvoolab commented Sep 2, 2026

Copy link
Copy Markdown

The public v2 schema declares five inbox routes. The CLI reaches one of them.

GET /public/v2/inbox/                            -> {transactions_count, records_count}
GET /public/v2/inbox/transactions/               -> {items, count}
GET /public/v2/inbox/records/                    -> {items, count}
GET /public/v2/inbox/transactions/{record_id}/   -> requires query transaction_type
GET /public/v2/inbox/records/{record_id}/        -> requires query record_type

main.py registers inbox against path inbox, so dualentry inbox list calls the
summary route. That response carries no items/count, so format_output sends it to
_print_detail and the command prints a two-row table of counts. With --all, paginate
finds no items key, stops on the first page and the command prints "No results."

The two detail routes cannot be served by the generic factory at all: both take a required
type discriminator as a query parameter, and the factory's get sends only the path. #24
notes this in its description.

What this adds

New commands/inbox.py, wired as two sub-groups under the existing inbox group
(hand-written module, following the commands/accounts.py precedent):

dualentry inbox transactions list [--transaction-type ... --status ... --company ...
                                   --customer ... --search --start-date --end-date
                                   --min-amount --max-amount --limit --offset --all]
dualentry inbox transactions get <record_id> --transaction-type <type>
dualentry inbox records list      [--record-type ... --status ... --search --limit --offset --all]
dualentry inbox records get      <record_id> --record-type <type>

Filter flags are the ones the two list routes declare in the schema, minus the by-id
lookups (record_id, transaction_id); repeatable ones are sent as repeated query
params. --status maps to approval_status, which is what these routes call it.

Two smaller things ride along because the new commands are the first to need them:

  • _resume_all_command now repeats a flag for a list-valued filter instead of printing
    the Python list, so the --all resume hint stays copy-pasteable.
  • get prints "This record is not in the inbox." instead of an empty table when the API
    answers with an empty object, which the schema documents as the not-in-inbox case.
    --format json still prints {} so scripts are unaffected.

Happy to drop either of those into a separate PR if you would rather keep this to the four
commands.

Not touched

  • inbox get stays unregistered — tests/test_stale_commands.py locks that, and the lock
    still passes (sub-groups land in registered_groups, not registered_commands).
  • inbox list still calls the summary route, unchanged.
  • No dependency changes, so uv.lock is untouched.

Verification

uv run pytest                 ->  252 passed, 125 skipped   (baseline 242 passed)
uv run ruff check .           ->  All checks passed
uv run ruff format --check .  ->  31 files already formatted

Ten new tests in tests/test_commands.py::TestInboxCommands, mock-based like the rest of
that file: route and params for each of the four commands, the required discriminator
(exit code 2 without it), the empty-object path in both output formats, the resume-hint
repeat, and a regression guard that inbox list still hits /inbox/.

One limitation worth stating plainly: I have no credentials for the API, so every test is
mocked and none of this has been run against a live inbox response.

The public v2 schema declares five inbox routes; the CLI reached one of them.
`inbox list` calls the summary endpoint, which returns counts rather than items,
and the two detail routes take a required type discriminator that the generic
`get` cannot send.

Adds commands/inbox.py, wired as two sub-groups under the existing inbox group
following the commands/accounts.py precedent:

  inbox transactions list|get <id> --transaction-type <type>
  inbox records      list|get <id> --record-type <type>

Filter flags are the ones the two list routes declare in the schema, minus the
by-id lookups; repeatable ones are sent as repeated query params, and --status
maps to approval_status.

Two smaller fixes ride along because these commands are the first to need them:
_resume_all_command repeats a flag for a list-valued filter instead of printing
the Python list, so the --all resume hint stays copy-pasteable; and get reports
"This record is not in the inbox." rather than an empty table when the API
answers with an empty object, while --format json still prints {}.

inbox get stays unregistered, so tests/test_stale_commands.py keeps its lock.
No dependency changes.
@Warkanlock

Copy link
Copy Markdown
Contributor

Thanks for your contributions!

@Warkanlock

Copy link
Copy Markdown
Contributor

Reproducible proof required

This PR description has no reproducible-proof artifact — no image, screenshot, screen recording, curl example, or documented reproduction/verification steps.

Please edit the PR description to include evidence the change works, then mark the PR ready for review.

Recommended for this PR: reproduction steps plus the command output (test run, curl, logs, or a screenshot) proving the change works.

Any of these count as proof:

  • Screenshot or before/after image of the affected UI
  • Short screen recording (Loom / uploaded video)
  • curl request + response for API changes
  • Copy-pasted test output or logs under a Test plan / Steps to reproduce heading

Moved to draft until proof is added.

@Warkanlock
Warkanlock marked this pull request as draft September 2, 2026 12:43
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