bookalimo 0.1.5__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. bookalimo/__init__.py +17 -24
  2. bookalimo/_version.py +9 -0
  3. bookalimo/client.py +310 -0
  4. bookalimo/config.py +16 -0
  5. bookalimo/exceptions.py +115 -5
  6. bookalimo/integrations/__init__.py +1 -0
  7. bookalimo/integrations/google_places/__init__.py +31 -0
  8. bookalimo/integrations/google_places/client_async.py +258 -0
  9. bookalimo/integrations/google_places/client_sync.py +257 -0
  10. bookalimo/integrations/google_places/common.py +245 -0
  11. bookalimo/integrations/google_places/proto_adapter.py +224 -0
  12. bookalimo/{_logging.py → logging.py} +45 -42
  13. bookalimo/schemas/__init__.py +97 -0
  14. bookalimo/schemas/base.py +56 -0
  15. bookalimo/{models.py → schemas/booking.py} +88 -100
  16. bookalimo/schemas/places/__init__.py +37 -0
  17. bookalimo/schemas/places/common.py +198 -0
  18. bookalimo/schemas/places/google.py +596 -0
  19. bookalimo/schemas/places/place.py +337 -0
  20. bookalimo/services/__init__.py +11 -0
  21. bookalimo/services/pricing.py +191 -0
  22. bookalimo/services/reservations.py +227 -0
  23. bookalimo/transport/__init__.py +7 -0
  24. bookalimo/transport/auth.py +41 -0
  25. bookalimo/transport/base.py +44 -0
  26. bookalimo/transport/httpx_async.py +230 -0
  27. bookalimo/transport/httpx_sync.py +230 -0
  28. bookalimo/transport/retry.py +102 -0
  29. bookalimo/transport/utils.py +59 -0
  30. bookalimo-1.0.0.dist-info/METADATA +307 -0
  31. bookalimo-1.0.0.dist-info/RECORD +35 -0
  32. bookalimo/_client.py +0 -420
  33. bookalimo/wrapper.py +0 -444
  34. bookalimo-0.1.5.dist-info/METADATA +0 -392
  35. bookalimo-0.1.5.dist-info/RECORD +0 -12
  36. {bookalimo-0.1.5.dist-info → bookalimo-1.0.0.dist-info}/WHEEL +0 -0
  37. {bookalimo-0.1.5.dist-info → bookalimo-1.0.0.dist-info}/licenses/LICENSE +0 -0
  38. {bookalimo-0.1.5.dist-info → bookalimo-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,56 @@
1
+ """
2
+ Shared base model for Book-A-Limo API data structures.
3
+ Handles automatic snake_case <-> camelCase conversion and enum serialization.
4
+ """
5
+
6
+ from enum import Enum
7
+ from typing import Any, cast
8
+
9
+ from pydantic import BaseModel, ConfigDict, model_serializer
10
+ from pydantic.alias_generators import to_camel
11
+ from pydantic_core.core_schema import SerializationInfo, SerializerFunctionWrapHandler
12
+
13
+
14
+ class ApiModel(BaseModel):
15
+ """
16
+ Base model for all Book-A-Limo API models.
17
+
18
+ Provides automatic field name conversion between Python snake_case
19
+ and API camelCase, plus proper enum handling.
20
+ """
21
+
22
+ model_config = ConfigDict(
23
+ # Auto-generate camelCase aliases from snake_case field names
24
+ alias_generator=to_camel,
25
+ # Accept both alias (camel) and name (snake) on input (v2.11+)
26
+ validate_by_alias=True,
27
+ validate_by_name=True, # replaces populate_by_name
28
+ # Default to using aliases when dumping (wire format)
29
+ serialize_by_alias=True,
30
+ # Enums dumping handled in model_serializer
31
+ use_enum_values=False,
32
+ # Ignore unknown keys from the API
33
+ extra="ignore",
34
+ )
35
+
36
+ @model_serializer(mode="wrap")
37
+ def _serialize(
38
+ self, handler: SerializerFunctionWrapHandler, info: SerializationInfo
39
+ ) -> dict[str, Any]:
40
+ # Run the normal serialization first (aliases, include/exclude, nested models, etc.)
41
+ data = handler(self)
42
+
43
+ # Decide how to emit enums based on context (default to 'value')
44
+ enum_out = (info.context or {}).get("enum_out", "value")
45
+
46
+ def convert(obj: Any) -> Any:
47
+ if isinstance(obj, Enum):
48
+ return obj.name if enum_out == "name" else obj.value
49
+ if isinstance(obj, dict):
50
+ return {k: convert(v) for k, v in obj.items()}
51
+ if isinstance(obj, (list, tuple)):
52
+ t = type(obj)
53
+ return t(convert(v) for v in obj)
54
+ return obj
55
+
56
+ return cast(dict[str, Any], convert(data))
@@ -1,20 +1,31 @@
1
1
  """
2
- Pydantic models for Book-A-Limo API data structures.
2
+ Pydantic schemas for Book-A-Limo booking operations.
3
+ Includes all models related to pricing, reservations, and booking requests.
3
4
  """
4
5
 
5
- import hashlib
6
6
  import warnings
7
7
  from enum import Enum
8
+ from functools import lru_cache
8
9
  from typing import Any, Optional
9
10
 
10
11
  import airportsdata
11
12
  import pycountry
12
13
  import us
13
- from pydantic import BaseModel, Field, model_validator
14
+ from pydantic import Field, model_validator
14
15
  from typing_extensions import Self
15
16
 
16
- icao_data = airportsdata.load("ICAO")
17
- iata_data = airportsdata.load("IATA")
17
+ from .base import ApiModel
18
+
19
+
20
+ @lru_cache(maxsize=1)
21
+ def _load_iata_index() -> tuple[dict[str, Any], dict[str, list[str]]]:
22
+ """Load and index airport data."""
23
+ data = airportsdata.load("IATA")
24
+ by_country: dict[str, list[str]] = {}
25
+ for code, rec in data.items():
26
+ c = (rec.get("country") or "").upper()
27
+ by_country.setdefault(c, []).append(code)
28
+ return data, by_country
18
29
 
19
30
 
20
31
  class RateType(Enum):
@@ -67,7 +78,7 @@ class ReservationStatus(Enum):
67
78
  class CardHolderType(Enum):
68
79
  """
69
80
  Credit card holder types (API Documentation Unclear).
70
- TODO: Update when API documentation is clarified by Ivan.
81
+ TODO: Update when API documentation is clarified by the API author.
71
82
  Best guess based on typical credit card processing:
72
83
  """
73
84
 
@@ -77,28 +88,13 @@ class CardHolderType(Enum):
77
88
  UNKNOWN = 3 # From example in API doc
78
89
 
79
90
 
80
- class Credentials(BaseModel):
81
- """User credentials for API authentication."""
82
-
83
- id: str
84
- is_customer: bool = False
85
- password_hash: str
86
-
87
- @classmethod
88
- def create_hash(cls, password: str, user_id: str) -> str:
89
- """Create password hash as required by API: Sha256(Sha256(Password) + LowerCase(Id))"""
90
- inner_hash = hashlib.sha256(password.encode()).hexdigest()
91
- full_string = inner_hash + user_id.lower()
92
- return hashlib.sha256(full_string.encode()).hexdigest()
93
-
94
-
95
- class City(BaseModel):
91
+ class City(ApiModel):
96
92
  """City information."""
97
93
 
98
94
  city_name: str
99
95
  country_code: str = Field(..., description="ISO 3166-1 alpha-2 country code")
100
- state_code: Optional[str] = Field(None, description="US state code")
101
- state_name: Optional[str] = Field(None, description="US state name")
96
+ state_code: Optional[str] = Field(default=None, description="US state code")
97
+ state_name: Optional[str] = Field(default=None, description="US state name")
102
98
 
103
99
  @model_validator(mode="after")
104
100
  def validate_country_code(self) -> Self:
@@ -111,6 +107,9 @@ class City(BaseModel):
111
107
  @model_validator(mode="after")
112
108
  def validate_us(self) -> Self:
113
109
  """Validate that state_code is a valid US state code and state_name is a valid US state name."""
110
+ if self.country_code != "US":
111
+ return self
112
+
114
113
  code_match = us.states.lookup(str(self.state_code))
115
114
  name_match = us.states.lookup(str(self.state_name))
116
115
  if not code_match and not name_match:
@@ -131,28 +130,28 @@ class City(BaseModel):
131
130
  )
132
131
 
133
132
 
134
- class Address(BaseModel):
133
+ class Address(ApiModel):
135
134
  """
136
135
  Address information.
137
-
138
- Note: API documentation inconsistency - mentions 'googlePlaceId' in text
139
- but examples use 'googleGeocode'. Following examples and using google_geocode.
140
- TODO: Confirm with API author if this should be googlePlaceId instead.
141
136
  """
142
137
 
143
138
  google_geocode: Optional[dict[str, Any]] = Field(
144
- None, description="Raw Google Geocoding API response (recommended)"
139
+ default=None, description="Raw Google Geocoding API response (recommended)"
145
140
  )
146
141
  city: Optional[City] = Field(
147
- None, description="Use only if google_geocode not available"
142
+ default=None, description="Use only if google_geocode not available"
143
+ )
144
+ district: Optional[str] = Field(default=None, description="e.g., Manhattan")
145
+ neighbourhood: Optional[str] = Field(
146
+ default=None, description="e.g., Lower Manhattan"
148
147
  )
149
- district: Optional[str] = Field(None, description="e.g., Manhattan")
150
- neighbourhood: Optional[str] = Field(None, description="e.g., Lower Manhattan")
151
- place_name: Optional[str] = Field(None, description="e.g., Empire State Building")
152
- street_name: Optional[str] = Field(None, description="e.g., East 34th St")
153
- building: Optional[str] = Field(None, description="e.g., 53")
154
- suite: Optional[str] = Field(None, description="e.g., 5P")
155
- zip: Optional[str] = Field(None, description="e.g., 10016")
148
+ place_name: Optional[str] = Field(
149
+ default=None, description="e.g., Empire State Building"
150
+ )
151
+ street_name: Optional[str] = Field(default=None, description="e.g., East 34th St")
152
+ building: Optional[str] = Field(default=None, description="e.g., 53")
153
+ suite: Optional[str] = Field(default=None, description="e.g., 5P")
154
+ zip: Optional[str] = Field(default=None, description="e.g., 10016")
156
155
 
157
156
  @model_validator(mode="after")
158
157
  def validate_address(self) -> Self:
@@ -173,37 +172,39 @@ class Address(BaseModel):
173
172
 
174
173
  if self.city:
175
174
  warnings.warn(
176
- "Not recommended. Use only if you can't provide googleGeocode.",
175
+ "Using google_geocode instead of city is recommended.",
177
176
  stacklevel=3,
178
177
  )
179
178
 
180
179
  return self
181
180
 
182
181
 
183
- class Airport(BaseModel):
182
+ class Airport(ApiModel):
184
183
  """Airport information."""
185
184
 
186
185
  iata_code: str = Field(..., description="3-letter IATA code, e.g., JFK")
187
- country_code: Optional[str] = Field(None, description="ISO 3166-1 alpha-2")
188
- state_code: Optional[str] = Field(None, description="US state code, e.g., NY")
186
+ country_code: Optional[str] = Field(default=None, description="ISO 3166-1 alpha-2")
187
+ state_code: Optional[str] = Field(
188
+ default=None, description="US state code, e.g., NY"
189
+ )
189
190
  airline_iata_code: Optional[str] = Field(
190
- None, description="2-letter IATA airline code"
191
+ default=None, description="2-letter IATA airline code"
191
192
  )
192
193
  airline_icao_code: Optional[str] = Field(
193
- None, description="3-letter ICAO airline code"
194
+ default=None, description="3-letter ICAO airline code"
194
195
  )
195
- flight_number: Optional[str] = Field(None, description="e.g., UA1234")
196
- terminal: Optional[str] = Field(None, description="e.g., 7")
196
+ flight_number: Optional[str] = Field(default=None, description="e.g., UA1234")
197
+ terminal: Optional[str] = Field(default=None, description="e.g., 7")
197
198
  arriving_from_city: Optional[City] = None
198
199
  meet_greet: Optional[int] = Field(
199
- None,
200
+ default=None,
200
201
  description="Meet & greet option ID. Leave empty on price request to see options.",
201
202
  )
202
203
 
203
204
  @model_validator(mode="after")
204
205
  def validate_country_code(self) -> Self:
205
206
  """Validate that country_code is a valid ISO 3166-1 alpha-2 country code."""
206
- if not pycountry.countries.get(alpha_2=self.country_code):
207
+ if self.country_code and not pycountry.countries.get(alpha_2=self.country_code):
207
208
  raise ValueError(f"Invalid country code: {self.country_code}")
208
209
 
209
210
  return self
@@ -218,16 +219,14 @@ class Airport(BaseModel):
218
219
 
219
220
  @model_validator(mode="after")
220
221
  def validate_airport(self) -> Self:
221
- """Validate that iata_code or airline_icao_code is a valid IATA or ICAO code."""
222
- if self.iata_code and self.iata_code not in iata_data:
222
+ """Validate that iata_code is a valid IATA code."""
223
+ if self.iata_code not in _load_iata_index()[0]:
223
224
  raise ValueError(f"Invalid IATA code: {self.iata_code}")
224
- if self.airline_icao_code and self.airline_icao_code not in icao_data:
225
- raise ValueError(f"Invalid ICAO code: {self.airline_icao_code}")
226
225
 
227
226
  return self
228
227
 
229
228
 
230
- class Location(BaseModel):
229
+ class Location(ApiModel):
231
230
  """Location (address or airport)."""
232
231
 
233
232
  type: LocationType
@@ -245,14 +244,14 @@ class Location(BaseModel):
245
244
  return self
246
245
 
247
246
 
248
- class Stop(BaseModel):
247
+ class Stop(ApiModel):
249
248
  """Stop information."""
250
249
 
251
250
  description: str = Field(..., description="Address, place name, or comment")
252
251
  is_en_route: bool = Field(..., description="True if stop is en-route")
253
252
 
254
253
 
255
- class Account(BaseModel):
254
+ class Account(ApiModel):
256
255
  """Travel agency or corporate account info."""
257
256
 
258
257
  id: str = Field(..., description="TA or corporate account number")
@@ -260,10 +259,10 @@ class Account(BaseModel):
260
259
  booker_first_name: Optional[str] = None
261
260
  booker_last_name: Optional[str] = None
262
261
  booker_email: Optional[str] = None
263
- booker_phone: Optional[str] = Field(None, description="E164 format")
262
+ booker_phone: Optional[str] = Field(default=None, description="E164 format")
264
263
 
265
264
 
266
- class Passenger(BaseModel):
265
+ class Passenger(ApiModel):
267
266
  """Passenger information."""
268
267
 
269
268
  first_name: str
@@ -272,14 +271,14 @@ class Passenger(BaseModel):
272
271
  phone: str = Field(..., description="E164 format recommended")
273
272
 
274
273
 
275
- class Reward(BaseModel):
274
+ class Reward(ApiModel):
276
275
  """Reward account information."""
277
276
 
278
277
  type: RewardType
279
278
  value: str = Field(..., description="Reward account number")
280
279
 
281
280
 
282
- class CreditCard(BaseModel):
281
+ class CreditCard(ApiModel):
283
282
  """Credit card information."""
284
283
 
285
284
  number: str
@@ -288,12 +287,12 @@ class CreditCard(BaseModel):
288
287
  card_holder: str
289
288
  zip: Optional[str] = None
290
289
  holder_type: Optional[CardHolderType] = Field(
291
- None,
290
+ default=None,
292
291
  description="Card holder type - API documentation unclear, using best guess",
293
292
  )
294
293
 
295
294
 
296
- class BreakdownItem(BaseModel):
295
+ class BreakdownItem(ApiModel):
297
296
  """Price breakdown item."""
298
297
 
299
298
  name: str
@@ -303,14 +302,14 @@ class BreakdownItem(BaseModel):
303
302
  )
304
303
 
305
304
 
306
- class MeetGreetAdditional(BaseModel):
305
+ class MeetGreetAdditional(ApiModel):
307
306
  """Additional meet & greet charges."""
308
307
 
309
308
  name: str
310
309
  price: float
311
310
 
312
311
 
313
- class MeetGreet(BaseModel):
312
+ class MeetGreet(ApiModel):
314
313
  """Meet & greet option."""
315
314
 
316
315
  id: int
@@ -323,7 +322,7 @@ class MeetGreet(BaseModel):
323
322
  reservation_price: float
324
323
 
325
324
 
326
- class Price(BaseModel):
325
+ class Price(ApiModel):
327
326
  """Car class pricing information."""
328
327
 
329
328
  car_class: str
@@ -339,7 +338,7 @@ class Price(BaseModel):
339
338
  meet_greets: list[MeetGreet] = Field(default_factory=list)
340
339
 
341
340
 
342
- class Reservation(BaseModel):
341
+ class Reservation(ApiModel):
343
342
  """Basic reservation information."""
344
343
 
345
344
  confirmation_number: str
@@ -356,7 +355,7 @@ class Reservation(BaseModel):
356
355
  status: Optional[ReservationStatus] = None
357
356
 
358
357
 
359
- class EditableReservationRequest(BaseModel):
358
+ class EditableReservationRequest(ApiModel):
360
359
  """
361
360
  Editable reservation for modifications.
362
361
 
@@ -368,11 +367,12 @@ class EditableReservationRequest(BaseModel):
368
367
  confirmation: str
369
368
  is_cancel_request: bool = False
370
369
  rate_type: Optional[RateType] = None
371
- pickup_date: Optional[str] = Field(None, description="MM/dd/yyyy format")
372
- pickup_time: Optional[str] = Field(None, description="hh:mm tt format")
370
+ pickup_date: Optional[str] = Field(default=None, description="MM/dd/yyyy format")
371
+ pickup_time: Optional[str] = Field(default=None, description="hh:mm tt format")
373
372
  stops: Optional[list[Stop]] = None
374
373
  credit_card: Optional[CreditCard] = Field(
375
- None, description="Conditionally required - unclear from API docs when exactly"
374
+ default=None,
375
+ description="Conditionally required - unclear from API docs when exactly",
376
376
  )
377
377
  passengers: Optional[int] = None
378
378
  luggage: Optional[int] = None
@@ -380,36 +380,30 @@ class EditableReservationRequest(BaseModel):
380
380
  car_seats: Optional[int] = None
381
381
  boosters: Optional[int] = None
382
382
  infants: Optional[int] = None
383
- other: Optional[str] = Field(None, description="Other changes not listed")
384
-
385
-
386
- class EditableReservationRequestAuthenticated(EditableReservationRequest):
387
- """Request for editing reservation with authenticated credentials."""
388
-
389
- credentials: Credentials
383
+ other: Optional[str] = Field(default=None, description="Other changes not listed")
390
384
 
391
385
 
392
386
  # Request/Response Models
393
387
 
394
388
 
395
- class PriceRequest(BaseModel):
389
+ class PriceRequest(ApiModel):
396
390
  """Request for getting prices."""
397
391
 
398
392
  rate_type: RateType
399
393
  date_time: str = Field(..., description="MM/dd/yyyy hh:mm tt format")
400
394
  pickup: Location
401
395
  dropoff: Location
402
- hours: Optional[int] = Field(None, description="For hourly rate_type only")
396
+ hours: Optional[int] = Field(default=None, description="For hourly rate_type only")
403
397
  passengers: int
404
398
  luggage: int
405
399
  stops: Optional[list[Stop]] = None
406
400
  account: Optional[Account] = Field(
407
- None, description="TAs must provide for commission"
401
+ default=None, description="TAs must provide for commission"
408
402
  )
409
403
  passenger: Optional[Passenger] = None
410
404
  rewards: Optional[list[Reward]] = None
411
405
  car_class_code: Optional[str] = Field(
412
- None, description="e.g., 'SD' for specific car class"
406
+ default=None, description="e.g., 'SD' for specific car class"
413
407
  )
414
408
  pets: Optional[int] = None
415
409
  car_seats: Optional[int] = None
@@ -418,20 +412,14 @@ class PriceRequest(BaseModel):
418
412
  customer_comment: Optional[str] = None
419
413
 
420
414
 
421
- class PriceRequestAuthenticated(PriceRequest):
422
- """Request for getting prices with authenticated credentials."""
423
-
424
- credentials: Credentials
425
-
426
-
427
- class PriceResponse(BaseModel):
415
+ class PriceResponse(ApiModel):
428
416
  """Response from get prices."""
429
417
 
430
418
  token: str
431
419
  prices: list[Price]
432
420
 
433
421
 
434
- class DetailsRequest(BaseModel):
422
+ class DetailsRequest(ApiModel):
435
423
  """Request for setting reservation details."""
436
424
 
437
425
  token: str
@@ -448,23 +436,25 @@ class DetailsRequest(BaseModel):
448
436
  infants: Optional[int] = None
449
437
  customer_comment: Optional[str] = None
450
438
  ta_fee: Optional[float] = Field(
451
- None, description="For Travel Agencies - additional fee in USD"
439
+ default=None, description="For Travel Agencies - additional fee in USD"
452
440
  )
453
441
 
454
442
 
455
- class DetailsResponse(BaseModel):
443
+ class DetailsResponse(ApiModel):
456
444
  """Response from set details."""
457
445
 
458
446
  price: float
459
447
  breakdown: list[BreakdownItem]
460
448
 
461
449
 
462
- class BookRequest(BaseModel):
450
+ class BookRequest(ApiModel):
463
451
  """Request for booking reservation."""
464
452
 
465
453
  token: str
466
454
  promo: Optional[str] = None
467
- method: Optional[str] = Field(None, description="'charge' for charge accounts")
455
+ method: Optional[str] = Field(
456
+ default=None, description="'charge' for charge accounts"
457
+ )
468
458
  credit_card: Optional[CreditCard] = None
469
459
 
470
460
  @model_validator(mode="after")
@@ -476,20 +466,19 @@ class BookRequest(BaseModel):
476
466
  return self
477
467
 
478
468
 
479
- class BookResponse(BaseModel):
469
+ class BookResponse(ApiModel):
480
470
  """Response from book reservation."""
481
471
 
482
472
  reservation_id: str
483
473
 
484
474
 
485
- class ListReservationsRequest(BaseModel):
475
+ class ListReservationsRequest(ApiModel):
486
476
  """Request for listing reservations."""
487
477
 
488
- credentials: Credentials
489
478
  is_archive: bool = False
490
479
 
491
480
 
492
- class ListReservationsResponse(BaseModel):
481
+ class ListReservationsResponse(ApiModel):
493
482
  """Response from list reservations."""
494
483
 
495
484
  success: bool
@@ -497,14 +486,13 @@ class ListReservationsResponse(BaseModel):
497
486
  error: Optional[str] = None
498
487
 
499
488
 
500
- class GetReservationRequest(BaseModel):
489
+ class GetReservationRequest(ApiModel):
501
490
  """Request for getting reservation details."""
502
491
 
503
- credentials: Credentials
504
492
  confirmation: str
505
493
 
506
494
 
507
- class GetReservationResponse(BaseModel):
495
+ class GetReservationResponse(ApiModel):
508
496
  """Response from get reservation."""
509
497
 
510
498
  reservation: EditableReservationRequest
@@ -526,7 +514,7 @@ class GetReservationResponse(BaseModel):
526
514
  pending_changes: list[list[str]] = Field(default_factory=list)
527
515
 
528
516
 
529
- class EditReservationResponse(BaseModel):
517
+ class EditReservationResponse(ApiModel):
530
518
  """Response from edit reservation."""
531
519
 
532
520
  success: bool
@@ -0,0 +1,37 @@
1
+ """Google Places API schemas."""
2
+
3
+ from .google import (
4
+ AutocompletePlacesRequest,
5
+ AutocompletePlacesResponse,
6
+ Circle,
7
+ FormattableText,
8
+ GeocodingRequest,
9
+ GetPlaceRequest,
10
+ LocationBias,
11
+ LocationRestriction,
12
+ Place,
13
+ PlacePrediction,
14
+ PlaceType,
15
+ QueryPrediction,
16
+ StringRange,
17
+ StructuredFormat,
18
+ Suggestion,
19
+ )
20
+
21
+ __all__ = [
22
+ "PlaceType",
23
+ "StringRange",
24
+ "FormattableText",
25
+ "StructuredFormat",
26
+ "Circle",
27
+ "LocationBias",
28
+ "LocationRestriction",
29
+ "Place",
30
+ "AutocompletePlacesResponse",
31
+ "PlacePrediction",
32
+ "QueryPrediction",
33
+ "Suggestion",
34
+ "GetPlaceRequest",
35
+ "AutocompletePlacesRequest",
36
+ "GeocodingRequest",
37
+ ]