Refresh empty OAuth2 credentials in batch request
Batch request serialization requires the credentials in the http
object to be applied to every individual request *prior* to
actually invoking http.request(). This circumvents the self-refresh
logic in the request() function monkey-patched by the OAuth2
credentials, thus resulting in the use of an uninitialized credentials
object.
The fix here is to detect when OAuth2-like credentials are being
used and to force a refresh in case the access token is not
initialized yet.
Resolves: #211
diff --git a/googleapiclient/http.py b/googleapiclient/http.py
index 5181bad..d22313f 100644
--- a/googleapiclient/http.py
+++ b/googleapiclient/http.py
@@ -1394,6 +1394,14 @@
if http is None:
raise ValueError("Missing a valid http object.")
+ # Special case for OAuth2Credentials-style objects which have not yet been
+ # refreshed with an initial access_token.
+ if getattr(http.request, 'credentials', None) is not None:
+ creds = http.request.credentials
+ if not getattr(creds, 'access_token', None):
+ LOGGER.info('Attempting refresh to obtain initial access_token')
+ creds.refresh(http)
+
self._execute(http, self._order, self._requests)
# Loop over all the requests and check for 401s. For each 401 request the
diff --git a/tests/test_http.py b/tests/test_http.py
index 0845047..1cca7c6 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -1072,6 +1072,34 @@
header = parts[1].splitlines()[1]
self.assertEqual('Content-Type: application/http', header)
+ def test_execute_initial_refresh_oauth2(self):
+ batch = BatchHttpRequest()
+ callbacks = Callbacks()
+ cred = MockCredentials('Foo')
+
+ # Pretend this is a OAuth2Credentials object
+ cred.access_token = None
+
+ http = HttpMockSequence([
+ ({'status': '200',
+ 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
+ BATCH_SINGLE_RESPONSE),
+ ])
+
+ cred.authorize(http)
+
+ batch.add(self.request1, callback=callbacks.f)
+ batch.execute(http=http)
+
+ self.assertEqual({'foo': 42}, callbacks.responses['1'])
+ self.assertIsNone(callbacks.exceptions['1'])
+
+ self.assertEqual(1, cred._refreshed)
+
+ self.assertEqual(1, cred._authorized)
+
+ self.assertEqual(1, cred._applied)
+
def test_execute_refresh_and_retry_on_401(self):
batch = BatchHttpRequest()
callbacks = Callbacks()