Add support for Google Default Credentials.
diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py
index 01cf02f..53ff945 100644
--- a/googleapiclient/discovery.py
+++ b/googleapiclient/discovery.py
@@ -146,7 +146,8 @@
discoveryServiceUrl=DISCOVERY_URI,
developerKey=None,
model=None,
- requestBuilder=HttpRequest):
+ requestBuilder=HttpRequest,
+ credentials=None):
"""Construct a Resource for interacting with an API.
Construct a Resource object for interacting with an API. The serviceName and
@@ -166,6 +167,8 @@
model: googleapiclient.Model, converts to and from the wire format.
requestBuilder: googleapiclient.http.HttpRequest, encapsulator for an HTTP
request.
+ credentials: oauth2client.Credentials, credentials to be used for
+ authentication.
Returns:
A Resource object with methods for interacting with the service.
@@ -204,7 +207,8 @@
raise InvalidJsonError()
return build_from_document(content, base=discoveryServiceUrl, http=http,
- developerKey=developerKey, model=model, requestBuilder=requestBuilder)
+ developerKey=developerKey, model=model, requestBuilder=requestBuilder,
+ credentials=credentials)
@positional(1)
@@ -215,7 +219,8 @@
http=None,
developerKey=None,
model=None,
- requestBuilder=HttpRequest):
+ requestBuilder=HttpRequest,
+ credentials=None):
"""Create a Resource for interacting with an API.
Same as `build()`, but constructs the Resource object from a discovery
@@ -236,6 +241,7 @@
model: Model class instance that serializes and de-serializes requests and
responses.
requestBuilder: Takes an http request and packages it up to be executed.
+ credentials: object, credentials to be used for authentication.
Returns:
A Resource object with methods for interacting with the service.
@@ -249,6 +255,27 @@
base = urlparse.urljoin(service['rootUrl'], service['servicePath'])
schema = Schemas(service)
+ if credentials:
+ # If credentials were passed in, we could have two cases:
+ # 1. the scopes were specified, in which case the given credentials
+ # are used for authorizing the http;
+ # 2. the scopes were not provided (meaning the Default Credentials are
+ # to be used). In this case, the Default Credentials are built and
+ # used instead of the original credentials. If there are no scopes
+ # found (meaning the given service requires no authentication), there is
+ # no authorization of the http.
+ if credentials.create_scoped_required():
+ scopes = service.get('auth', {}).get('oauth2', {}).get('scopes', {})
+ if scopes:
+ credentials = credentials.create_scoped(scopes.keys())
+ else:
+ # No need to authorize the http object
+ # if the service does not require authentication.
+ credentials = None
+
+ if credentials:
+ http = credentials.authorize(http)
+
if model is None:
features = service.get('features', [])
model = JsonModel('dataWrapper' in features)
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index 3d30d8c..bc7836f 100644
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -516,6 +516,20 @@
self.assertTrue(getattr(plus, 'activities'))
self.assertTrue(getattr(plus, 'people'))
+ def test_credentials(self):
+ class CredentialsMock:
+ def create_scoped_required(self):
+ return False
+
+ def authorize(self, http):
+ http.orest = True
+
+ self.http = HttpMock(datafile('plus.json'), {'status': '200'})
+ build('plus', 'v1', http=self.http, credentials=None)
+ self.assertFalse(hasattr(self.http, 'orest'))
+ build('plus', 'v1', http=self.http, credentials=CredentialsMock())
+ self.assertTrue(hasattr(self.http, 'orest'))
+
def test_full_featured(self):
# Zoo should exercise all discovery facets
# and should also have no future.json file.