Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ env
googlemaps.egg-info
*.egg
.vscode/
.idea/
47 changes: 44 additions & 3 deletions googlemaps/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,21 @@
except ImportError: # Python 2
from urllib import urlencode


_X_GOOG_MAPS_EXPERIENCE_ID = "X-Goog-Maps-Experience-ID"
_USER_AGENT = "GoogleGeoApiClientPython/%s" % googlemaps.__version__
_DEFAULT_BASE_URL = "https://maps.googleapis.com"

_RETRIABLE_STATUSES = set([500, 503, 504])


class Client(object):
"""Performs requests to the Google Maps API web services."""

def __init__(self, key=None, client_id=None, client_secret=None,
timeout=None, connect_timeout=None, read_timeout=None,
retry_timeout=60, requests_kwargs=None,
queries_per_second=50, channel=None,
retry_over_query_limit=True):
retry_over_query_limit=True, experience_id=None):
"""
:param key: Maps API key. Required, unless "client_id" and
"client_secret" are set.
Expand Down Expand Up @@ -99,6 +100,10 @@ def __init__(self, key=None, client_id=None, client_secret=None,
retried. Defaults to True.
:type retry_over_query_limit: bool

:param experience_id: The value for the HTTP header field name
'X-Goog-Maps-Experience-ID'.
:type experience_id: str

:raises ValueError: when either credentials are missing, incomplete
or invalid.
:raises NotImplementedError: if connect_timeout and read_timeout are
Expand Down Expand Up @@ -150,7 +155,7 @@ def __init__(self, key=None, client_id=None, client_secret=None,
self.retry_timeout = timedelta(seconds=retry_timeout)
self.requests_kwargs = requests_kwargs or {}
headers = self.requests_kwargs.pop('headers', {})
headers.update({"User-Agent": _USER_AGENT})
headers.update({"User-Agent": _USER_AGENT})
self.requests_kwargs.update({
"headers": headers,
"timeout": self.timeout,
Expand All @@ -160,6 +165,42 @@ def __init__(self, key=None, client_id=None, client_secret=None,
self.queries_per_second = queries_per_second
self.retry_over_query_limit = retry_over_query_limit
self.sent_times = collections.deque("", queries_per_second)
self.set_experience_id(experience_id)

def set_experience_id(self, *experience_id_args):
"""Sets the value for the HTTP header field name
'X-Goog-Maps-Experience-ID' to be used on subsequent API calls.

:param experience_id_args: the experience ID
:type experience_id_args: string varargs
"""
if len(experience_id_args) == 0 or experience_id_args[0] is None:
self.clear_experience_id()
return

headers = self.requests_kwargs.pop("headers", {})
headers[_X_GOOG_MAPS_EXPERIENCE_ID] = ",".join(experience_id_args)
self.requests_kwargs["headers"] = headers

def get_experience_id(self):
"""Gets the experience ID for the HTTP header field name
'X-Goog-Maps-Experience-ID'

:return: The experience ID if set
:rtype: str
"""
headers = self.requests_kwargs.get("headers", {})
return headers.get(_X_GOOG_MAPS_EXPERIENCE_ID, None)

def clear_experience_id(self):
"""Clears the experience ID for the HTTP header field name
'X-Goog-Maps-Experience-ID' if set.
"""
headers = self.requests_kwargs.get("headers")
if headers is None:
return
headers.pop(_X_GOOG_MAPS_EXPERIENCE_ID, {})
self.requests_kwargs["headers"] = headers

def _request(self, url, params, first_request_time=None, retry_counter=0,
base_url=_DEFAULT_BASE_URL, accepts_clientid=True,
Expand Down
83 changes: 83 additions & 0 deletions googlemaps/test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@

import responses
import requests
import uuid

import googlemaps
import googlemaps.client as _client
import googlemaps.test as _test
from googlemaps.client import _X_GOOG_MAPS_EXPERIENCE_ID


class ClientTest(_test.TestCase):
Expand Down Expand Up @@ -291,6 +293,87 @@ def test_requests_version(self):
googlemaps.Client(**client_args_timeout)
googlemaps.Client(**client_args)

def test_single_experience_id(self):
experience_id1 = "Exp1"
client = googlemaps.Client(key="AIzaasdf", experience_id=experience_id1)
self.assertEqual(experience_id1, client.get_experience_id())

experience_id2 = "Exp2"
client.set_experience_id(experience_id2)
self.assertEqual(experience_id2, client.get_experience_id())

def test_multiple_experience_id(self):
client = googlemaps.Client(key="AIzaasdf")

experience_id1 = "Exp1"
experience_id2 = "Exp2"
client.set_experience_id(experience_id1, experience_id2)

result = "%s,%s" % (experience_id1, experience_id2)
self.assertEqual(result, client.get_experience_id())

def test_no_experience_id(self):
client = googlemaps.Client(key="AIzaasdf")
self.assertIsNone(client.get_experience_id())

def test_clearing_experience_id(self):
client = googlemaps.Client(key="AIzaasdf")
client.set_experience_id("ExpId")
client.clear_experience_id()
self.assertIsNone(client.get_experience_id())

def test_experience_id_sample(self):
# [START maps_experience_id]
experience_id = str(uuid.uuid4())

# instantiate client with experience id
client = googlemaps.Client(
key="AIza-Maps-API-Key",
experience_id=experience_id
)

# clear the current experience id
client.clear_experience_id()

# set a new experience id
other_experience_id = str(uuid.uuid4())
client.set_experience_id(experience_id, other_experience_id)

# make API request, the client will set the header
# X-GOOG-MAPS-EXPERIENCE-ID: experience_id,other_experience_id

# get current experience id
ids = client.get_experience_id()
# [END maps_experience_id]

result = "%s,%s" % (experience_id, other_experience_id)
self.assertEqual(result, ids)

@responses.activate
def _perform_mock_request(self, experience_id=None):
# Mock response
responses.add(responses.GET,
"https://maps.googleapis.com/maps/api/geocode/json",
body='{"status":"OK","results":[]}',
status=200,
content_type="application/json")

# Perform network call
client = googlemaps.Client(key="AIzaasdf")
client.set_experience_id(experience_id)
client.geocode("Sesame St.")
return responses.calls[0].request

def test_experience_id_in_header(self):
experience_id = "Exp1"
request = self._perform_mock_request(experience_id)
header_value = request.headers[_X_GOOG_MAPS_EXPERIENCE_ID]
self.assertEqual(experience_id, header_value)

def test_experience_id_no_in_header(self):
request = self._perform_mock_request()
self.assertIsNone(request.headers.get(_X_GOOG_MAPS_EXPERIENCE_ID))

@responses.activate
def test_no_retry_over_query_limit(self):
responses.add(responses.GET,
Expand Down