Skip to content

feat(places): add Places API (New) support (v1)#252

Merged
maximn merged 1 commit into
masterfrom
feat/places-api-new
May 30, 2026
Merged

feat(places): add Places API (New) support (v1)#252
maximn merged 1 commit into
masterfrom
feat/places-api-new

Conversation

@maximn

@maximn maximn commented May 30, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the modern Places API (New) (places.googleapis.com/v1) alongside the frozen legacy Places endpoints, with full parity across all five surfaces and a comprehensive Place model. Follows the Routes API blueprint — no engine changes required, because MapsAPIGenericEngine already issues a POST whenever a request supplies a body (GetRequestBody() returns non-null HttpContent) and a GET otherwise.

Surfaces

Surface HTTP Endpoint Facade
Text Search POST v1/places:searchText PlacesSearchText
Nearby Search POST v1/places:searchNearby PlacesSearchNearby
Place Details GET v1/places/{placeId} PlaceDetailsNew
Autocomplete POST v1/places:autocomplete PlacesAutocompleteNew
Photo media GET v1/{photoName}/media PlacePhoto
  • API key + field mask ride in the query string (key, $fields), as Routes does.
  • Photo uses skipHttpRedirect=true so Google returns JSON { name, photoUri } on the existing string-deserialize path (no binary handling).

Public surface

  • New facade accessors on the static GoogleMaps and on GoogleMapsClient/IGoogleMapsClient: PlacesSearchText, PlacesSearchNearby, PlaceDetailsNew, PlacesAutocompleteNew, PlacePhoto.
  • New entities under GoogleMapsApi/Entities/PlacesNew/ (Common/Request/Response), including a comprehensive Place model (address components, opening hours, photos, reviews, payment/parking/accessibility/fuel/EV-charge options, etc.).

Deprecation

Legacy Places request types and their facade properties are marked [Obsolete] (warning, not error) pointing to the new namespace. Our own backward-compat plumbing is wrapped in #pragma warning disable CS0618 so the build stays clean (TreatWarningsAsErrors is false).

Tests & docs

  • Offline unit tests (PlacesNewUnitTests): URI host/path, POST-vs-GET, $fields presence/absence, key, request-body field names & null-omission, required-arg validation, skipHttpRedirect, enum [EnumMember] round-trip, and Place deserialization from a captured JSON sample. 20/20 pass; Routes unit tests still 11/11.
  • Live integration tests (IntegrationTests/PlacesNewTests) added (not run in CI without a key).
  • README Supported APIs table updated (new rows + legacy rows annotated deprecated). CHANGELOG.md intentionally untouched (auto-generated by release.sh).

Notes

  • Build is green across all six target frameworks (net10.0/net8.0/net6.0/netstandard2.0/net481/net462).
  • No source-generated JsonSerializerContext exists in the repo yet; if the AOT PR (Develop/aot il2026 #247) merges first, the new PlacesNew.Response types will need registration there.

…ment)

Add the modern Places API (New) (places.googleapis.com/v1) alongside the
frozen legacy Places endpoints, with full parity across all five surfaces and
a comprehensive Place model. Follows the Routes API blueprint — no engine
changes needed, since the HTTP engine already issues a POST whenever a request
supplies a body and a GET otherwise.

Surfaces:
- Text Search    POST v1/places:searchText      -> SearchTextResponse
- Nearby Search  POST v1/places:searchNearby     -> SearchNearbyResponse
- Place Details  GET  v1/places/{placeId}        -> Place
- Autocomplete   POST v1/places:autocomplete     -> AutocompleteResponse
- Photo media    GET  v1/{photoName}/media       -> PlacePhotoResponse

API key and field mask ride in the query string (key, $fields); the photo
endpoint uses skipHttpRedirect=true so Google returns JSON ({ name, photoUri })
on the existing string-deserialize path rather than raw image bytes.

Facade accessors (static GoogleMaps + GoogleMapsClient/IGoogleMapsClient):
PlacesSearchText, PlacesSearchNearby, PlaceDetailsNew, PlacesAutocompleteNew,
PlacePhoto.

Mark the legacy Places request types and facade properties [Obsolete] (warning)
steering users to the new namespace; backward-compat plumbing is wrapped in
pragma CS0618 so the build stays clean.

Add offline unit tests (URI/method/field-mask/body/validation/enum round-trip/
Place deserialization) and live integration tests. README updated; CHANGELOG
left to release.sh per project convention.
@maximn maximn enabled auto-merge (squash) May 30, 2026 18:24
@github-actions

Copy link
Copy Markdown

🔬 TestGlance

build · ✅ 242 passed — 100.0%

████████████████ 100.0%
⏱️ 21.2s


vs master

Metric master PR Delta
Pass rate 100.0% 100.0% +0.0%
Duration 19.6s 21.2s +8.0%

📄 HTML Report


Updated 2026-05-30T18:24:32.947Z

@maximn maximn disabled auto-merge May 30, 2026 18:24
@maximn

maximn commented May 30, 2026

Copy link
Copy Markdown
Owner Author

Code review

Found 2 issues:

  1. Every [Obsolete] message on the legacy Places classes and facades points migrating callers to GoogleMaps.PlacesSearchText, even for endpoints whose real replacement is different — PlacesNearByRequest/PlacesRequestPlacesSearchNearby, PlaceAutocompleteRequestPlacesAutocompleteNew, PlacesDetailsRequestPlaceDetailsNew. The same wrong hint is repeated across all six legacy request classes and their properties in GoogleMaps.cs/IGoogleMapsClient.cs, so a developer following the compiler hint for e.g. nearby search is sent to the text-search endpoint.

/// </summary>
[Obsolete("The legacy Places API is frozen. Use the Places API (New) — e.g. GoogleMaps.PlacesSearchText and the GoogleMapsApi.Entities.PlacesNew namespace.")]
public class PlacesNearByRequest : MapsBaseRequest
{

  1. RankPreference documents the Nearby Search default as Distance, but the Places API (New) default for Nearby Search is POPULARITY (Text Search defaults to RELEVANCE, which is correct). The enum is also missing a Popularity member entirely, so callers cannot request popularity ranking and a POPULARITY value cannot round-trip through deserialization.

/// <summary>
/// How search results should be ranked. Shared by Text Search and Nearby Search; note that the
/// default differs by surface (Text Search defaults to <see cref="Relevance"/>, Nearby Search to
/// <see cref="Distance"/>).
/// </summary>
public enum RankPreference
{
/// <summary>Rank preference unspecified; the API picks a per-surface default.</summary>
[EnumMember(Value = "RANK_PREFERENCE_UNSPECIFIED")] Unspecified,
/// <summary>Rank results by distance from the query location.</summary>
[EnumMember(Value = "DISTANCE")] Distance,
/// <summary>Rank results by relevance.</summary>
[EnumMember(Value = "RELEVANCE")] Relevance,
}

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@maximn maximn merged commit bd543f4 into master May 30, 2026
6 checks passed
@maximn maximn deleted the feat/places-api-new branch May 30, 2026 19:54
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