diff --git a/CHANGELOG.md b/CHANGELOG.md index 9456382..25ad47d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,16 @@ that may never merge. They are not releases and are not listed here. ## Unreleased +- `mapbox places get`/`batch`, full place detail — hours, phone, website, + photos, address, coordinates, activity data — by the `mapbox_id` a + Search Box API result already returned. Hand-authored into + `custom-openapi/` for the same reason this session's other additions + were: no upstream spec exists yet. `batch` takes `--data + '{"ids": [...]}'`, up to 100 ids in one call, the same shape `styles + create` already uses for a body with no sensible per-field flag. Both + operations verified against production with real ids from + `search forward`. + - `mapbox ev-charge-finder search`/`get`/`list-operators`, EV charging stations near a point — searchable by connector type, operator, charging power, availability, amenities and payment method — plus one station's diff --git a/README.md b/README.md index da88da6..64391b5 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ mapbox geocoder * mapbox isochrone * mapbox map-matching * mapbox matrix * +mapbox places * mapbox search * mapbox sprites * mapbox static * diff --git a/custom-openapi/places/openapi/places.yaml b/custom-openapi/places/openapi/places.yaml new file mode 100644 index 0000000..5e8189c --- /dev/null +++ b/custom-openapi/places/openapi/places.yaml @@ -0,0 +1,107 @@ +openapi: "3.0.0" +# `parse_spec` turns `info.description` below into this service's clap +# `long_about`, so it also reaches `mapbox places --help`, `--schema` and +# `generate-skills` output. Keep it to API prose only — the provenance +# below is for whoever edits this file, not for a CLI user: +# +# Hand-authored down to the parameters documented at +# docs.mapbox.com/api/search/places. See `custom-openapi/README.md` for +# how a file like this is wired in. +info: + title: "Mapbox Places API" + description: >- + Full detail for a place — hours, phone, website, photos, address, + coordinates, activity data — by the `mapbox_id` a Search Box API result + already returned. This API has no search or suggest of its own; it only + resolves ids something else found. + version: "0.0.0" +servers: + - url: https://api.mapbox.com + description: Places API +paths: + /places/v1/details/retrieve/{mapbox_id}: + get: + operationId: get + summary: Full detail for one place. + description: >- + `mapbox_id` comes from a Search Box API result (`search forward`/ + `reverse`/`category`) — this API does not look places up by name + or location itself. + parameters: + - name: "mapbox_id" + in: path + required: true + description: "The place's Mapbox ID, from a Search Box API result." + schema: + type: string + minLength: 1 + - name: "access_token" + in: query + required: true + description: "Mapbox API Access Token" + schema: + type: string + minLength: 1 + responses: + "200": + description: >- + A place record: `name`, `full_address`, `primary_category`/ + `categories`, `coordinates` (with `routable_points`), + structured `address`, `score` (`closed`/`reality`/ + `popularity`, each 0-1), and where available `brand`, + `opening_hours`, `phone`, `photos`, `website`, `building`, and + `telemetry` (hourly activity by day of week). + "401": + description: Unauthorized + "404": + description: Not Found — no place with that Mapbox ID. + "422": + description: Unprocessable Entity — `mapbox_id` isn't validly formed. + "429": + description: >- + Too Many Requests — the per-second rate limit, or the Public + Preview's monthly quota (1000 records/account), was exceeded. + + /places/v1/details/retrieve: + post: + operationId: batch + summary: Full detail for up to 100 places in one call. + description: >- + Hydrates a whole list of Search Box API results in one round trip + instead of one `get` per id. `--data` carries `{"ids": [...]}` — + up to 100 `mapbox_id` strings. + parameters: + - name: "access_token" + in: query + required: true + description: "Mapbox API Access Token" + schema: + type: string + minLength: 1 + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + "200": + description: >- + Every id resolved: `{"results": [, ...]}`, same + shape as `get`'s own response, one entry per id. + "206": + description: >- + Partial Content — some ids didn't resolve. Same body as 200, + plus `missing` (ids with no such place) and/or `unprocessed` + (ids that couldn't be processed) alongside `results`. + "400": + description: >- + Bad Request — `ids` isn't a non-empty array of at most 100 + entries. + "401": + description: Unauthorized + "429": + description: >- + Too Many Requests — the per-second rate limit (batch is + charged by id count) or the Public Preview's monthly quota was + exceeded. diff --git a/docs/commands.md b/docs/commands.md index ca6a79b..c9071c8 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,6 +1,6 @@ # Implemented commands -Every command the CLI ships: four auth commands, 40 API operations across 14 +Every command the CLI ships: four auth commands, 42 API operations across 15 command groups, the tilesets-cli proxy, `completion` and `generate-skills`. Each is shown in both of its renderings. Which one you get is decided by `--output`, whose default (`auto`) reads stdout: a terminal gets the left column, a pipe or redirect @@ -10,10 +10,11 @@ gets the right one. See Account names, style ids and tokens in the examples are replaced; everything else is as the API sent it. -**33 of the 40 were run against the live API and show what came back:** +**35 of the 42 were run against the live API and show what came back:** `directions route`, `isochrone contours`, `map-matching match`, `matrix -compute`, `feedback list` and `feedback get` on 2026-09-24, once those -command groups existed at all, and the rest earlier — `fonts list`, +compute`, `feedback list`, `feedback get`, `places get` and `places batch` +on 2026-09-24/25, once those command groups existed at all, and the rest +earlier — `fonts list`, `fonts upload` and `fonts delete` on 2026-09-08 once `fonts:list`/`fonts:write` became registrable, the remainder before that. The write operations were exercised as round trips on throwaway objects — @@ -33,13 +34,16 @@ commands and the flags they take — is held to `mapbox --schema` on every can be checked cannot fall behind the binary. The remaining 7 give the response shape from the spec or the docs instead -of a live capture. Four are `search`'s — read-only and safe to run, but the -credentials used to write this page have no Search Box API access, so -every call answers 401 rather than a result. The other three are -`ev-charge-finder`'s: that API is Private Preview, and this account isn't -enrolled — confirmed directly (a plain `curl` gets `401 invalid access -token` there and `200` on every other service with the same token), not -assumed. +of a live capture. Three are `ev-charge-finder`'s: that API is Private +Preview, and this account isn't enrolled — confirmed directly (a plain +`curl` gets `401 invalid access token` there and `200` on every other +service with the same token), not assumed. The other four are `search`'s, +marked as no-access when this page was first written — no longer true, +discovered while writing `places get`'s example above, which needed a real +`search forward` result to test against and got one. `search`'s own four +sections below haven't been re-captured with this pass, since that's a +different command group's page to touch; a worthwhile follow-up, not done +here. Each **Parameters** section lists only what is specific to its command. The globals every API command takes are @@ -105,6 +109,9 @@ nests, and is typed `mapbox styles draft get`. **[Matrix](#matrix)** — [matrix.compute](#mapbox-matrix-compute) +**[Places](#places)** — [places.get](#mapbox-places-get) · +[places.batch](#mapbox-places-batch) + **[Search](#search)** — [search.forward](#mapbox-search-forward) · [search.reverse](#mapbox-search-reverse) · [search.category](#mapbox-search-category) · @@ -1837,6 +1844,138 @@ Neither output mode has a bespoke rendering for this response, same as `-o text` pretty-printed and `-o json` on one line. Dropped each waypoint's own snap `distance` for length; the real response carries it too. +--- +## Places + +Full detail for a place — hours, phone, website, photos, address, +coordinates, activity data — by the `mapbox_id` a Search Box API result +already returned. Curated by hand down to the parameters documented at +docs.mapbox.com/api/search/places — see `custom-openapi/README.md` for why +this command group doesn't come from the vendored specs the way most +others do. + +**This API has no search or suggest of its own.** It only resolves ids +`search forward`/`reverse`/`category` already returned — the detail-view +follow-up to a search result, not a way to find places by name or location. + +### `mapbox places get` + +Full detail for one place. + +#### Parameters + +`` (positional) is required — from a Search Box API result. + +#### Examples + +```sh +mapbox places get dXJuOm1ieHBvaTpmYTE5Y2NhMC0yZmQ3LTQwMzgtYTEzNy02MzFmNGEwZDI5ODA +``` + +#### Outputs + +Captured live, a real place found via `search forward --q "Ferry Building +San Francisco"`: + + + + +
Terminal — -o textAgent — -o json
+ +```json +{ + "mapbox_id": "dXJuOm1ieHBvaTpmYTE5Y2NhMC0yZmQ3LTQwMzgtYTEzNy02MzFmNGEwZDI5ODA", + "name": "Ferry Building", + "full_address": "San Francisco, California, 94105, United States", + "primary_category": "food", + "categories": ["cafe", "food", "food_and_drink"], + "status": "active", + "permanently_closed": false, + "opening_hours": "Sa 08:00-14:00", + "phone": "+14152373318", + "website": "http://crumbleandwhisk.com/", + "score": { "closed": 0, "reality": 0.973, "popularity": 0.275 }, + "coordinates": { + "latitude": 37.79557765, + "longitude": -122.39332918, + "source": "poi", + "routable_points": [ + { "name": "driving", "latitude": 37.795594, "longitude": -122.393338 } + ] + }, + "address": { + "city": "San Francisco", + "neighborhood": "Financial District", + "postcode": "94105", + "region": "California", + "region_code_full": "US-CA", + "country": "United States", + "country_code": "US" + } +} +``` + + + +```json +{"mapbox_id":"dXJuOm1ieHBvaTpmYTE5Y2NhMC0yZmQ3LTQwMzgtYTEzNy02MzFmNGEwZDI5ODA","name":"Ferry Building","full_address":"San Francisco, California, 94105, United States","primary_category":"food","categories":["cafe","food","food_and_drink"],"status":"active","permanently_closed":false,"opening_hours":"Sa 08:00-14:00","phone":"+14152373318","website":"http://crumbleandwhisk.com/","score":{"closed":0,"reality":0.973,"popularity":0.275},"coordinates":{"latitude":37.79557765,"longitude":-122.39332918,"source":"poi","routable_points":[{"name":"driving","latitude":37.795594,"longitude":-122.393338}]},"address":{"city":"San Francisco","neighborhood":"Financial District","postcode":"94105","region":"California","region_code_full":"US-CA","country":"United States","country_code":"US"}} +``` + +
+ +Dropped `attributes` (14 boolean amenity flags — wheelchair access, payment +types, and the like), `created_at`/`updated_at`, and most `address` fields +that were `null` for this place, for length; the real response carries +them too. `brand` is `null` here since this isn't a chain location. + +### `mapbox places batch` + +Full detail for up to 100 places in one call — hydrates a whole list of +search results in one round trip instead of one `get` per id. + +#### Parameters + +`--data`/`-d` carries `{"ids": [...]}`, up to 100 `mapbox_id` strings. + +#### Examples + +```sh +mapbox places batch -d '{"ids": ["dXJuOm1ieHBvaTpmYTE5Y2NhMC0yZmQ3LTQwMzgtYTEzNy02MzFmNGEwZDI5ODA", "dXJuOm1ieHBvaTo4N2YzMmY2YS00MjkwLTQzNmItYWQyMi1hMzBhMzcxNWVmNzM"]}' +``` + +#### Outputs + +Captured live, the same Ferry Building above plus a second real place: + + + + +
Terminal — -o textAgent — -o json
+ +```json +{ + "results": [ + { "mapbox_id": "…fa19cca0…", "name": "Ferry Building" }, + { "mapbox_id": "…87f32f6a…", "name": "Golden Gate Bridge" } + ] +} +``` + + + +```json +{"results":[{"mapbox_id":"…fa19cca0…","name":"Ferry Building"},{"mapbox_id":"…87f32f6a…","name":"Golden Gate Bridge"}]} +``` + +
+ +Each entry in `results` is the full record `get` returns, trimmed to +`mapbox_id`/`name` here for length. Not captured live: `206` with +`missing`/`unprocessed` alongside `results` — the documented shape for a +batch where some ids didn't resolve. Both ids used to write this page were +real and resolved, so triggering it would have meant fabricating a +plausibly-shaped but fake id, which defeats the point of a live capture. + --- ## Search diff --git a/src/remedy.rs b/src/remedy.rs index 912de8f..0d6f812 100644 --- a/src/remedy.rs +++ b/src/remedy.rs @@ -98,6 +98,7 @@ const SERVICE_DOCS: &[(&str, &str)] = &[ "https://docs.mapbox.com/api/navigation/map-matching/", ), ("matrix", "https://docs.mapbox.com/api/navigation/matrix/"), + ("places", "https://docs.mapbox.com/api/search/places/"), ("search", "https://docs.mapbox.com/api/search/search-box/"), // Static Images and Static Tiles merged into one `static` command group // (#116); neither upstream page covers both, so this points at Static diff --git a/src/spec.rs b/src/spec.rs index bc8d9ac..ce0e990 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -686,6 +686,10 @@ pub const CUSTOM_SPEC_ENTRIES: &[SpecEntry] = &[ name: "ev-charge-finder", yaml: include_str!("../custom-openapi/ev-charge-finder/openapi/ev-charge-finder.yaml"), }, + SpecEntry { + name: "places", + yaml: include_str!("../custom-openapi/places/openapi/places.yaml"), + }, ]; /// The list the CLI actually generates commands from: [`MAPBOX_SPEC_ENTRIES`], diff --git a/tests/fixtures/api_command_surface.txt b/tests/fixtures/api_command_surface.txt index fa0f672..7ab7f0c 100644 --- a/tests/fixtures/api_command_surface.txt +++ b/tests/fixtures/api_command_surface.txt @@ -16,6 +16,8 @@ mapbox geocoder reverse | aliases: (none) mapbox isochrone contours | aliases: (none) mapbox map-matching match | aliases: (none) mapbox matrix compute | aliases: (none) +mapbox places batch | aliases: (none) +mapbox places get | aliases: (none) mapbox search category | aliases: (none) mapbox search forward | aliases: (none) mapbox search list-category | aliases: (none)