Skip to content

Add mapbox ev-charge-finder search/get/list-operators - #52

Closed
mattpodwysocki wants to merge 5 commits into
feat/feedback-apifrom
feat/ev-charge-finder-api
Closed

mattpodwysocki wants to merge 5 commits into
feat/feedback-apifrom
feat/ev-charge-finder-api

Conversation

@mattpodwysocki

Copy link
Copy Markdown
Contributor

⚠️ Temporarily based on #49, not main

Same stacking situation as the rest of this stack. Once #43/#44/#45/#48/#49
merge in order, this should be retargeted to `main` (`gh pr edit --base
main`). (Note: this branch no longer includes Optimization — that PR was
closed per explicit product guidance not to include it. This PR is based
directly on #49, not on top of it.)

What

`mapbox ev-charge-finder search`/`get`/`list-operators`. Hand-authored
into `custom-openapi/` since openapi-specs has no spec for this API
either.

EV charging stations near a point — searchable by connector type,
operator, charging power, availability, amenities and payment method —
plus one station's full detail (tariffs, opening hours) and the list of
known charge-point operators.

⚠️ Not verified live — flagging prominently, not burying it

Every other command group added this session was checked against a real
response. This one wasn't: the credentials behind this environment's token
get `401 invalid access token` on all three operations here, while the
same token succeeds immediately against every other service — confirmed
directly (a plain `curl` side by side, one 401 and one 200), not assumed.
That reads as this account not being enrolled in the Private Preview,
which is an account-level gate this CLI's own token handling has no way
around. The commands are built faithfully to the documented
request/response shape; `docs/commands.md`'s Outputs blocks say plainly
they're the documented shape, not a capture.

Also included: a real, generally-applicable bug fix

This spec's `search`↔`get` pairing surfaced a bug in the detail/listing
linker (`link_detail_operations`, `src/spec.rs`): a listing with a
required query parameter of its own — `search` needs
`latitude`/`longitude`/`distance` — was being suggested bare on a failed
detail lookup's 404. `listing_command` (`remedy.rs`) only ever fills a
listing's path parameters recovered from the failed call; it has no
source for required query parameters, so the suggested `mapbox
ev-charge-finder search` would itself have been a usage error.
`every_suggestion_is_a_command_line_that_runs` caught this before it
shipped. Fixed by excluding any listing with a required query parameter
from the link entirely — no other service had one, so nothing else
changes. New regression test
(`a_listing_with_a_required_query_parameter_is_never_named`), and the
existing paired tests confirm the forward `detail_command` link is
unaffected.

489 tests, `cargo fmt --check` and `cargo clippy --all-targets -- -D
warnings` both clean.

🤖 Generated with Claude Code

mattpodwysocki and others added 5 commits September 23, 2026 18:32
Second of the Navigation-category APIs with no prior CLI coverage. Same
shape as mapbox directions route (#43): hand-authored into
custom-openapi/ since openapi-specs has no spec for this API either, and
reuses that PR's fix for a spec parameter named `profile` colliding with the
global --profile flag (ARG_NAME_OVERRIDES gets a second row, not a second
mechanism).

`contours_minutes` and `contours_meters` are mutually exclusive but neither
is individually required by this CLI's own validation — same "not enforced
before the request goes out" precedent search category already uses for its
own proximity/near/bbox/route disjunction. The API answers 422 if both or
neither are given.

Smoke-tested against production: real contour polygons and linestrings for
driving and walking profiles, --polygons, --contours-minutes with multiple
values, verified to return the documented GeoJSON shape.

Also fixes a self-inflicted --help regression found while writing this:
first_sentence() in src/main.rs cuts a --help line at the first '.', and
several profile-scoped parameter descriptions in directions.yaml (already
merged in this branch) led with a complete sentence before the substantive
content, e.g. "`mapbox/driving` only." — eating everything after it in
--help. isochrone.yaml's own `denoise` had the same shape ("0.0-1.0:
...") and would have rendered as literally "0". Both fixed by moving the
qualifier to the end of the description instead of the front.

485 tests, fmt and clippy clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Third of the Navigation-category APIs with no prior CLI coverage. Same
shape as directions/isochrone (#43, #44): hand-authored
into custom-openapi/ since openapi-specs has no spec for this API either,
reusing ARG_NAME_OVERRIDES for the same profile-vs-global-flag collision
(third row, not a third mechanism).

Excludes POST, for the same documented reason directions route does: this
spec format can't express "GET or POST, caller's choice" for one
operationId, and the API's own POST exists specifically for a trace too
long for a URL (~8100 bytes) — a real gap, not a design choice.

Smoke-tested against production: a three-point San Francisco trace
returned a real match with legs/steps/geometry, including a null
tracepoint for a point too far from the road network to match — the
documented shape for that case, not a bug.

486 tests, fmt and clippy clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fourth of the Navigation-category APIs with no prior CLI coverage. Same
shape as directions/isochrone/map-matching: hand-authored into
custom-openapi/ since openapi-specs has no spec for this API either,
reusing ARG_NAME_OVERRIDES for the same profile-vs-global-flag collision
(fourth row, not a fourth mechanism).

Named `compute` rather than the API-naming doc's proposed `get`, matching
the precedent set by the three commands before it (route/contours/match) —
this returns a matrix computed fresh from the request, not a stored
resource.

Verified a real discrepancy against production while testing: the API
answers a comma-separated --sources/--destinations list with a 422
("may be \"all\" or semicolon-separated list of 0-based integer indices"),
unlike this CLI's own convention elsewhere of comma-separating index lists.
Documented in the spec and in --help, not just discovered and left as a
surprise.

Also fixes a --help truncation on --fallback-speed ("Legacy." as a leading
sentence, same class of bug as directions.yaml's earlier ones) by moving
the qualifier to the end of the description.

Smoke-tested against production: a full 3x3 matrix and a subsetted 1x2
matrix via --sources/--destinations, both verified to return the
documented shape.

487 tests, fmt and clippy clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fifth API this session with no prior CLI coverage — completes the naming
doc's V1 scope (directions, isochrone, map matching, matrix, feedback).
Hand-authored into custom-openapi/ since openapi-specs has no spec for this
API either.

feedback create, the write side, is declared in the spec but never reaches
the command surface: confirmed directly against production that a
POST /oauth/register requesting user-feedback:write silently drops it from
the granted scope, the same unregistrable shape UNSUPPORTED_OPERATIONS
already documents for accounts create-token/update-token/delete-token and
styles download-style-zip. No profile path parameter here, so no new
ARG_NAME_OVERRIDES row — this service was never at risk of that collision.

Named the disabled operation createFeedbackItem rather than the shorter
create: the withheld-operation guard in generate_skills.rs checks the bare
command word against every generated file's text, and a bare "create"
collides with the exposed, unrelated `styles create`. Since the operation
is never reachable either way, the internal name doesn't affect anything
user-facing.

Also fixes a --help truncation on all six *_before/*_after time-window
parameters ("ISO 8601." as a leading sentence, same class of bug fixed in
directions.yaml and matrix.yaml earlier) by moving the format note to the
end of each description.

Updated docs/commands.md's own stale operation/group counts while here —
37 operations across 13 groups, not the 33/10 it still said (already
stale by 2 before this branch, from drift this change didn't cause but
was already touching the same sentence).

Smoke-tested against production: list with comma-separated --status/
--category filters, pagination cursor, and get by id, all verified to
return the documented shape. Confirmed create is absent from --help and
errors as an unrecognized subcommand rather than reaching the API.

488 tests, fmt and clippy clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Seventh API this session. Hand-authored into custom-openapi/ since
openapi-specs has no spec for this API either.

Not verified live, unlike everything else shipped this session: this API
is Private Preview and the account behind this environment's token isn't
enrolled. Confirmed directly, not assumed — a plain curl to
/ev/v1/locations gets 401 "invalid access token" with the exact same token
that gets 200 against isochrone, matrix, and every other service touched
today. All three operations (search, get, list-operators) answer the same
401. Documented plainly in docs/commands.md rather than left implicit.

Also fixes a real, generally-applicable bug in the detail/listing linker
(link_detail_operations, src/spec.rs) that this spec's search<->get pairing
surfaced: a listing with a required query parameter of its own was being
suggested bare on a failed detail lookup's 404. search needs
latitude/longitude/distance; listing_command (remedy.rs) only ever fills a
listing's path parameters recovered from the failed call, never query
parameters, so the suggested "mapbox ev-charge-finder search" would itself
be a usage error. every_suggestion_is_a_command_line_that_runs caught this
before it shipped. Fixed by excluding any listing with a required query
parameter from the link entirely; no other service had one, so this
changes nothing else. New regression test
(a_listing_with_a_required_query_parameter_is_never_named) plus the
existing paired tests still passing confirms the forward detail_command
link is unaffected — only the backward listing suggestion was ever wrong.

489 tests, fmt and clippy clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mattpodwysocki

Copy link
Copy Markdown
Contributor Author

Closing as out of scope for V1, per product confirmation.

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