Skip to content

Commit

Permalink
Merge pull request #2970 from dhermes/cnxn-client-hygiene
Browse files Browse the repository at this point in the history
Making Connection()-s act as proxies for data stored in Client()-s
  • Loading branch information
dhermes authored Jan 30, 2017
2 parents f8ceb94 + 06914ce commit 3584917
Show file tree
Hide file tree
Showing 51 changed files with 380 additions and 456 deletions.
10 changes: 5 additions & 5 deletions bigquery/google/cloud/bigquery/_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@


class Connection(_http.JSONConnection):
"""A connection to Google BigQuery via the JSON REST API."""
"""A connection to Google BigQuery via the JSON REST API.
:type client: :class:`~google.cloud.bigquery.client.Client`
:param client: The client that owns the current connection.
"""

API_BASE_URL = 'https://www.googleapis.com'
"""The base of the API call URL."""
Expand All @@ -28,7 +32,3 @@ class Connection(_http.JSONConnection):

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

SCOPE = ('https://www.googleapis.com/auth/bigquery',
'https://www.googleapis.com/auth/cloud-platform')
"""The scopes required for authenticating as a BigQuery consumer."""
7 changes: 5 additions & 2 deletions bigquery/google/cloud/bigquery/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,14 @@ class Client(ClientWithProject):
``credentials`` for the current object.
"""

SCOPE = ('https://www.googleapis.com/auth/bigquery',
'https://www.googleapis.com/auth/cloud-platform')
"""The scopes required for authenticating as a BigQuery consumer."""

def __init__(self, project=None, credentials=None, http=None):
super(Client, self).__init__(
project=project, credentials=credentials, http=http)
self._connection = Connection(
credentials=self._credentials, http=self._http)
self._connection = Connection(self)

def list_projects(self, max_results=None, page_token=None):
"""List projects for the project associated with this client.
Expand Down
4 changes: 2 additions & 2 deletions bigquery/unit_tests/test__http.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _make_one(self, *args, **kw):
return self._get_target_class()(*args, **kw)

def test_build_api_url_no_extra_query_params(self):
conn = self._make_one()
conn = self._make_one(object())
URI = '/'.join([
conn.API_BASE_URL,
'bigquery',
Expand All @@ -40,7 +40,7 @@ def test_build_api_url_w_extra_query_params(self):
from six.moves.urllib.parse import parse_qsl
from six.moves.urllib.parse import urlsplit

conn = self._make_one()
conn = self._make_one(object())
uri = conn.build_api_url('/foo', {'bar': 'baz'})
scheme, netloc, path, qs, _ = urlsplit(uri)
self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL)
Expand Down
58 changes: 6 additions & 52 deletions core/google/cloud/_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@
import six
from six.moves.urllib.parse import urlencode

import google.auth.credentials
import google_auth_httplib2
import httplib2

from google.cloud.exceptions import make_exception


Expand All @@ -37,50 +33,14 @@
class Connection(object):
"""A generic connection to Google Cloud Platform.
Subclasses should understand only the basic types in method arguments,
however they should be capable of returning advanced types.
If no value is passed in for ``http``, a :class:`httplib2.Http` object
will be created and authorized with the ``credentials``. If not, the
``credentials`` and ``http`` need not be related.
Subclasses may seek to use the private key from ``credentials`` to sign
data.
A custom (non-``httplib2``) HTTP object must have a ``request`` method
which accepts the following arguments:
* ``uri``
* ``method``
* ``body``
* ``headers``
In addition, ``redirections`` and ``connection_type`` may be used.
Without the use of ``credentials.authorize(http)``, a custom ``http``
object will also need to be able to add a bearer token to API
requests and handle token refresh on 401 errors.
:type credentials: :class:`google.auth.credentials.Credentials` or
:class:`NoneType`
:param credentials: The credentials to use for this connection.
:type http: :class:`httplib2.Http` or class that defines ``request()``.
:param http: An optional HTTP object to make requests.
:type client: :class:`~google.cloud.client.Client`
:param client: The client that owns the current connection.
"""

USER_AGENT = DEFAULT_USER_AGENT

SCOPE = None
"""The scopes required for authenticating with a service.
Needs to be set by subclasses.
"""

def __init__(self, credentials=None, http=None):
self._http = http
self._credentials = google.auth.credentials.with_scopes_if_required(
credentials, self.SCOPE)
def __init__(self, client):
self._client = client

@property
def credentials(self):
Expand All @@ -90,7 +50,7 @@ def credentials(self):
:class:`NoneType`
:returns: The credentials object associated with this connection.
"""
return self._credentials
return self._client._credentials

@property
def http(self):
Expand All @@ -99,13 +59,7 @@ def http(self):
:rtype: :class:`httplib2.Http`
:returns: A Http object used to transport data.
"""
if self._http is None:
if self._credentials:
self._http = google_auth_httplib2.AuthorizedHttp(
self._credentials)
else:
self._http = httplib2.Http()
return self._http
return self._client._http


class JSONConnection(Connection):
Expand Down
44 changes: 42 additions & 2 deletions core/google/cloud/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import google.auth.credentials
from google.oauth2 import service_account
import google_auth_httplib2
import six

from google.cloud._helpers import _determine_default_project
Expand Down Expand Up @@ -74,6 +75,26 @@ class Client(_ClientFactoryMixin):
Stores ``credentials`` and ``http`` object so that subclasses
can pass them along to a connection class.
If no value is passed in for ``http``, a :class:`httplib2.Http` object
will be created and authorized with the ``credentials``. If not, the
``credentials`` and ``http`` need not be related.
Callers and subclasses may seek to use the private key from
``credentials`` to sign data.
A custom (non-``httplib2``) HTTP object must have a ``request`` method
which accepts the following arguments:
* ``uri``
* ``method``
* ``body``
* ``headers``
In addition, ``redirections`` and ``connection_type`` may be used.
A custom ``http`` object will also need to be able to add a bearer token
to API requests and handle token refresh on 401 errors.
:type credentials: :class:`~google.auth.credentials.Credentials`
:param credentials: (Optional) The OAuth2 Credentials to use for this
client. If not passed (and if no ``http`` object is
Expand All @@ -88,15 +109,34 @@ class Client(_ClientFactoryMixin):
``credentials`` for the current object.
"""

SCOPE = None
"""The scopes required for authenticating with a service.
Needs to be set by subclasses.
"""

def __init__(self, credentials=None, http=None):
if (credentials is not None and
not isinstance(
credentials, google.auth.credentials.Credentials)):
raise ValueError(_GOOGLE_AUTH_CREDENTIALS_HELP)
if credentials is None and http is None:
credentials = get_credentials()
self._credentials = credentials
self._http = http
self._credentials = google.auth.credentials.with_scopes_if_required(
credentials, self.SCOPE)
self._http_internal = http

@property
def _http(self):
"""Getter for object used for HTTP transport.
:rtype: :class:`~httplib2.Http`
:returns: An HTTP object.
"""
if self._http_internal is None:
self._http_internal = google_auth_httplib2.AuthorizedHttp(
self._credentials)
return self._http_internal


class _ClientProjectMixin(object):
Expand Down
Loading

0 comments on commit 3584917

Please sign in to comment.