Skip to content

feat: add connect devices reclaim subcommand#141

Open
lee-reinhardt wants to merge 1 commit intomainfrom
lee-eng-1843
Open

feat: add connect devices reclaim subcommand#141
lee-reinhardt wants to merge 1 commit intomainfrom
lee-eng-1843

Conversation

@lee-reinhardt
Copy link
Copy Markdown
Member

@lee-reinhardt lee-reinhardt commented May 7, 2026

Summary

Add a connect devices reclaim {list,approve,deny,delete} subcommand so operators can run the admin-approved device reclaim workflow from the terminal alongside the existing SPA surface.

This is opening as a draft because the workflow depends on the corresponding server-side reclaim flow, which has been implemented but is not yet rolled out. The CLI itself is complete and validated end-to-end against a local cluster — flagging draft so this doesn't get merged ahead of the runtime it depends on.

Command surface

avocado connect devices reclaim list      [--org] [--status pending|approved|completed|denied|expired|all] [--device-id <uuid>]
avocado connect devices reclaim approve <id>   [--org] [-y/--yes]
avocado connect devices reclaim deny    <id>   [--org] [--reason "..."] [-y/--yes]
avocado connect devices reclaim delete  <id>   [--org] [-y/--yes]

Common flags inherited from the existing connect devices pattern: --org, -C/--config <avocado.yaml>, --profile <name>.

Design choices

Workflow-shaped, not API-shaped. Verbs operators perform day-to-day, not generic CRUD primitives:

  • No show subcommand. list with --status filtering is the inspection surface; list defaults to --status pending (the "what needs my attention" view) and prints full uuids in the ID column so operators can copy-paste directly into approve/deny calls without a dedicated show command.
  • approve and deny ship together. They're the two outcomes of a single operator decision ("is this reset legit?"); shipping only approve would force deny back to the SPA mid-workflow, which is worse than the all-SPA status quo.
  • delete is the recovery path for a typo'd deny. A workflow that can deny but can't undo is incomplete; the API gates DELETE to denied-only rows so this is non-destructive for active records.

Error mapping

HTTP / code Readable message
422 not_pending "request is not pending (already approved, denied, completed, or expired)"
422 not_denied "only denied reclaim requests can be deleted"
422 claim_token_unavailable "the originating claim token is no longer available"
422 expired_token "the originating claim token has expired"
422 invalid_token "the originating claim token is no longer valid"
401 "not authenticated (run 'avocado connect auth login')"
403 "not authorized for this organization (your CLI token is scoped to a different org — check 'avocado connect auth status' or run 'avocado connect auth login' for the right org)"
404 "reclaim request not found"
any other falls through to HTTP {status}: {body} for triage

Tests

17 new unit tests:

  • validate_status accepts all 6 valid values and rejects unknowns with a useful error
  • device_label renders correctly across all 3 cases (name + identifier, identifier-only, device_id fallback)
  • format_reclaim_error maps all 5 known 422 codes plus 401, 403, 404
  • Fall-through paths: unrecognized 422 codes preserve the raw payload, unknown statuses include both code + body, non-JSON bodies don't panic

Test count: 917 (was 900).

Validation

  • cargo build clean
  • cargo clippy --all-targets -- -D warnings clean
  • cargo test --lib — all 917 tests pass
  • Manually walked through every command and error path against a local k3d cluster + a QEMU device daemon. Confirmed end-to-end:
    • Approve flow: device picks up new credentials within 30s, MQTT reconnects with same device_id (preserved across reclaim, only credentials rotate)
    • Deny flow: device receives terminal 403 reclaim_denied and idles cleanly without crash-looping
    • Delete flow: removes denied rows, allows fresh pending requests on subsequent device reset
    • Negative paths: each known error code maps to its readable message, no raw HTTP/JSON dumps

@lee-reinhardt lee-reinhardt marked this pull request as ready for review May 8, 2026 21:04
@lee-reinhardt lee-reinhardt requested review from Copilot and jasontwong and removed request for jasontwong May 8, 2026 21:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new avocado connect devices reclaim command group to let operators run the admin-approved device reclaim workflow from the CLI, integrating with the existing Connect client and error-mapping behavior.

Changes:

  • Introduces connect devices reclaim {list,approve,deny,delete} command surface and wires it into the CLI entrypoint.
  • Implements reclaim request listing + approve/deny/delete workflows, including interactive confirmations and formatted table output.
  • Extends ConnectClient with reclaim request API calls and adds reclaim-specific error mapping + unit tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/main.rs Adds the connect devices reclaim subcommands and dispatches to the new command implementations.
src/commands/connect/mod.rs Exposes the new device_reclaim module under commands::connect.
src/commands/connect/device_reclaim.rs Implements reclaim list/approve/deny/delete command logic and unit tests.
src/commands/connect/client.rs Adds reclaim request API models, client methods, and reclaim error parsing/mapping tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/commands/connect/client.rs
Comment thread src/commands/connect/device_reclaim.rs Outdated
Comment thread src/main.rs Outdated
Operators can now run the admin-approved device reclaim workflow from
the terminal, alongside the existing SPA surface:

  avocado connect devices reclaim list      [--status pending|approved|completed|denied|expired|all]
  avocado connect devices reclaim approve <id>  [-y]
  avocado connect devices reclaim deny    <id>  [--reason "..."] [-y]
  avocado connect devices reclaim delete  <id>  [-y]

Workflow-shaped (verbs operators perform), not API-shaped — no `show`
subcommand because `list` with filters covers inspection cleanly.
Approve and deny are the two outcomes of a single decision so they
ship together; delete is the recovery path for a typo'd deny. List
prints full uuids in the ID column so operators can copy-paste
without needing a separate show command.

Reuses the existing `ConnectClient` Bearer profile and `resolve_org()`
helpers; no new auth surface. Hits the existing reclaim endpoints
under /api/orgs/:org_id/reclaim-requests.

Errors from 422 (not_pending / not_denied / claim_token_unavailable /
expired_token / invalid_token), 401 (auth needed), 403 (org-scope
mismatch), and 404 (not found) are mapped to readable messages with
actionable guidance. Unrecognized statuses fall back to the raw HTTP
+ body for triage.

Tests: 17 new unit tests covering status validation, device label
rendering, and HTTP error mapping (each known error code, plus
fall-through behavior for unknowns).
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