Skip to content

Commit

Permalink
Add client_info support to client / connection. (#7872)
Browse files Browse the repository at this point in the history
  • Loading branch information
tseaver committed May 8, 2019
1 parent 2126d8e commit 616b77a
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 20 deletions.
14 changes: 9 additions & 5 deletions storage/google/cloud/storage/_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,22 @@
from google.cloud.storage import __version__


_CLIENT_INFO = _http.CLIENT_INFO_TEMPLATE.format(__version__)


class Connection(_http.JSONConnection):
"""A connection to Google Cloud Storage via the JSON REST API.
:type client: :class:`~google.cloud.storage.client.Client`
:param client: The client that owns the current connection.
:type client_info: :class:`~google.api_core.client_info.ClientInfo`
:param client_info: (Optional) instance used to generate user agent.
"""

def __init__(self, client, client_info=None):
super(Connection, self).__init__(client, client_info)

self._client_info.gapic_version = __version__
self._client_info.client_library_version = __version__

API_BASE_URL = _http.API_BASE_URL
"""The base of the API call URL."""

Expand All @@ -37,5 +43,3 @@ class Connection(_http.JSONConnection):

API_URL_TEMPLATE = "{api_base_url}/storage/{api_version}{path}"
"""A template for the URL of a particular API call."""

_EXTRA_HEADERS = {_http.CLIENT_INFO_HEADER: _CLIENT_INFO}
11 changes: 9 additions & 2 deletions storage/google/cloud/storage/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ class Client(ClientWithProject):
``credentials`` for the current object.
This parameter should be considered private, and could
change in the future.
:type client_info: :class:`~google.api_core.client_info.ClientInfo`
:param client_info:
The client info used to send a user-agent string along with API
requests. If ``None``, then default info will be used. Generally,
you only need to set this if you're developing your own library
or partner tool.
"""

SCOPE = (
Expand All @@ -60,7 +67,7 @@ class Client(ClientWithProject):
)
"""The scopes required for authenticating as a Cloud Storage consumer."""

def __init__(self, project=_marker, credentials=None, _http=None):
def __init__(self, project=_marker, credentials=None, _http=None, client_info=None):
self._base_connection = None
if project is None:
no_project = True
Expand All @@ -74,7 +81,7 @@ def __init__(self, project=_marker, credentials=None, _http=None):
)
if no_project:
self.project = None
self._connection = Connection(self)
self._connection = Connection(self, client_info=client_info)
self._batch_stack = _LocalStack()

@classmethod
Expand Down
6 changes: 2 additions & 4 deletions storage/tests/unit/test__http.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ def _make_one(self, *args, **kw):

def test_extra_headers(self):
import requests

from google.cloud import _http as base_http
from google.cloud.storage import _http as MUT

http = mock.create_autospec(requests.Session, instance=True)
response = requests.Response()
Expand All @@ -48,8 +46,8 @@ def test_extra_headers(self):

expected_headers = {
"Accept-Encoding": "gzip",
base_http.CLIENT_INFO_HEADER: MUT._CLIENT_INFO,
"User-Agent": conn.USER_AGENT,
base_http.CLIENT_INFO_HEADER: conn.user_agent,
"User-Agent": conn.user_agent,
}
expected_uri = conn.build_api_url("/rainbow")
http.request.assert_called_once_with(
Expand Down
38 changes: 29 additions & 9 deletions storage/tests/unit/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,50 +60,70 @@ def _make_one(self, *args, **kw):
return self._get_target_class()(*args, **kw)

def test_ctor_connection_type(self):
from google.cloud._http import ClientInfo
from google.cloud.storage._http import Connection

PROJECT = "PROJECT"
CREDENTIALS = _make_credentials()
credentials = _make_credentials()

client = self._make_one(project=PROJECT, credentials=CREDENTIALS)
client = self._make_one(project=PROJECT, credentials=credentials)

self.assertEqual(client.project, PROJECT)
self.assertIsInstance(client._connection, Connection)
self.assertIs(client._connection.credentials, CREDENTIALS)
self.assertIs(client._connection.credentials, credentials)
self.assertIsNone(client.current_batch)
self.assertEqual(list(client._batch_stack), [])
self.assertIsInstance(client._connection._client_info, ClientInfo)

def test_ctor_wo_project(self):
from google.cloud.storage._http import Connection

PROJECT = "PROJECT"
CREDENTIALS = _make_credentials()
credentials = _make_credentials()

ddp_patch = mock.patch(
"google.cloud.client._determine_default_project", return_value=PROJECT
)

with ddp_patch:
client = self._make_one(credentials=CREDENTIALS)
client = self._make_one(credentials=credentials)

self.assertEqual(client.project, PROJECT)
self.assertIsInstance(client._connection, Connection)
self.assertIs(client._connection.credentials, CREDENTIALS)
self.assertIs(client._connection.credentials, credentials)
self.assertIsNone(client.current_batch)
self.assertEqual(list(client._batch_stack), [])

def test_ctor_w_project_explicit_none(self):
from google.cloud.storage._http import Connection

CREDENTIALS = _make_credentials()
credentials = _make_credentials()

client = self._make_one(project=None, credentials=CREDENTIALS)
client = self._make_one(project=None, credentials=credentials)

self.assertIsNone(client.project)
self.assertIsInstance(client._connection, Connection)
self.assertIs(client._connection.credentials, credentials)
self.assertIsNone(client.current_batch)
self.assertEqual(list(client._batch_stack), [])

def test_ctor_w_client_info(self):
from google.cloud._http import ClientInfo
from google.cloud.storage._http import Connection

credentials = _make_credentials()
client_info = ClientInfo()

client = self._make_one(
project=None, credentials=credentials, client_info=client_info
)

self.assertIsNone(client.project)
self.assertIsInstance(client._connection, Connection)
self.assertIs(client._connection.credentials, CREDENTIALS)
self.assertIs(client._connection.credentials, credentials)
self.assertIsNone(client.current_batch)
self.assertEqual(list(client._batch_stack), [])
self.assertIs(client._connection._client_info, client_info)

def test_create_anonymous_client(self):
from google.auth.credentials import AnonymousCredentials
Expand Down

0 comments on commit 616b77a

Please sign in to comment.