Repository: googleapis/google-cloud-python

Issue:
Remove support for API_KEY in translation
See #2731


Discussion:
/cc @jonparrott for confirmation that we can safely remove it (per today's discussion).

@puneith our sample over at python-docs-samples still uses an API Key. Can we switch the sample over to service account credentials?

This still uses API_KEY [https://github.com/GoogleCloudPlatform/google-cloud-python/blob/master/translate/google/cloud/translate/client.py](https://github.com/GoogleCloudPlatform/google-cloud-python/blob/b92f0cf5af596dd41951a8505a7eea037fd1a48a/translate/google/cloud/translate/client.py)

@puneith The point (I think) was that by moving the sample away from the API key feature, we could more easily remove support for that feature in this library in future versions.

Got it. I will make the appropriate changes. 

I have recently started having problems making simple api-key translation calls. It works ok with one virtualenv, but running in a different virtualenv, and/or different project causes this error:

google.cloud.exceptions.Forbidden: 403 Google Translate API has not been used in project google.com:cloudsdktool before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/translate.googleapis.com/overview?project=google.com:cloudsdktool then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry. (GET https://translation.googleapis.com/language/translate/v2?target=en&q=mein&source=de)


It looks like it's trying to use the project cloudsdktool rather than the one I have setup using gcloud. Any idea how to fix this? It used to work so easily!!
@geoffwalmsley are you setting the `GOOGLE_APPLICATION_CREDENTIALS` environment variable?

Otherwise you should be able to set the project when creating the `Client(project='my-project')`.
@geoffwalmsley this is a quirk of how default credentials works. In your case the credentials are being supplied by the Cloud SDK. Unfortunately, these credentials are not sufficient for using the translate API. You'll need to download a service account key and use that.
@jonparrott Are you still opposed to removing support for API key? You were concerned that it was needed for Python clients which can't store secrets, though the API key is itself a secret.
I'm not opposed to removing it.

Resolving patch:
diff --git a/translate/google/cloud/translate/client.py b/translate/google/cloud/translate/client.py
--- a/translate/google/cloud/translate/client.py
+++ b/translate/google/cloud/translate/client.py
@@ -15,7 +15,6 @@
 """Client for interacting with the Google Cloud Translate API."""
 
 
-import httplib2
 import six
 
 from google.cloud._helpers import _to_bytes
@@ -41,10 +40,6 @@ class Client(BaseClient):
                             translations and language names. (Defaults to
                             :data:`ENGLISH_ISO_639`.)
 
-    :type api_key: str
-    :param api_key: (Optional) The key used to send with requests as a
-                    query parameter.
-
     :type credentials: :class:`oauth2client.client.OAuth2Credentials`
     :param credentials: (Optional) The OAuth2 Credentials to use for the
                         connection owned by this client. If not passed (and
@@ -58,16 +53,9 @@ class Client(BaseClient):
 
     _connection_class = Connection
 
-    def __init__(self, target_language=ENGLISH_ISO_639, api_key=None,
+    def __init__(self, target_language=ENGLISH_ISO_639,
                  credentials=None, http=None):
-        self.api_key = api_key
         self.target_language = target_language
-
-        if api_key is not None:
-            # If API key auth is desired, make it so that no credentials
-            # will be auto-detected by the base class constructor.
-            if http is None:
-                http = httplib2.Http()
         super(Client, self).__init__(credentials=credentials, http=http)
 
     def get_languages(self, target_language=None):
@@ -91,8 +79,6 @@ def get_languages(self, target_language=None):
                   language (localized to the target language).
         """
         query_params = {}
-        if self.api_key is not None:
-            query_params['key'] = self.api_key
         if target_language is None:
             target_language = self.target_language
         if target_language is not None:
@@ -137,8 +123,6 @@ def detect_language(self, values):
             values = [values]
 
         query_params = []
-        if self.api_key is not None:
-            query_params.append(('key', self.api_key))
         query_params.extend(('q', _to_bytes(value, 'utf-8'))
                             for value in values)
         response = self._connection.api_request(
@@ -230,8 +214,6 @@ def translate(self, values, target_language=None, format_=None,
             customization_ids = [customization_ids]
 
         query_params = [('target', target_language)]
-        if self.api_key is not None:
-            query_params.append(('key', self.api_key))
         query_params.extend(('q', _to_bytes(value, 'utf-8'))
                             for value in values)
         query_params.extend(('cid', cid) for cid in customization_ids)


Test patch:
diff --git a/translate/unit_tests/test_client.py b/translate/unit_tests/test_client.py
--- a/translate/unit_tests/test_client.py
+++ b/translate/unit_tests/test_client.py
@@ -17,8 +17,6 @@
 
 class TestClient(unittest.TestCase):
 
-    KEY = 'abc-123-my-key'
-
     @staticmethod
     def _get_target_class():
         from google.cloud.translate.client import Client
@@ -36,7 +34,6 @@ def test_constructor(self):
         self.assertIsInstance(client._connection, Connection)
         self.assertIsNone(client._connection.credentials)
         self.assertIs(client._connection.http, http)
-        self.assertIsNone(client.api_key)
         self.assertEqual(client.target_language, ENGLISH_ISO_639)
 
     def test_constructor_non_default(self):
@@ -44,34 +41,16 @@ def test_constructor_non_default(self):
 
         http = object()
         target = 'es'
-        client = self._make_one(
-            target_language=target, api_key=self.KEY, http=http)
+        client = self._make_one(target_language=target, http=http)
         self.assertIsInstance(client._connection, Connection)
         self.assertIsNone(client._connection.credentials)
         self.assertIs(client._connection.http, http)
-        self.assertEqual(self.KEY, client.api_key)
-        self.assertEqual(client.target_language, target)
-
-    def test_constructor_api_key_override(self):
-        import mock
-        from google.cloud.translate.connection import Connection
-
-        target = 'ru'
-        with mock.patch('httplib2.Http') as http_ctor:
-            client = self._make_one(
-                target_language=target, api_key=self.KEY)
-
-        http_ctor.assert_called_once_with()
-        self.assertIsInstance(client._connection, Connection)
-        self.assertIsNone(client._connection.credentials)
-        self.assertIs(client._connection.http, http_ctor.return_value)
-        self.assertEqual(self.KEY, client.api_key)
         self.assertEqual(client.target_language, target)
 
     def test_get_languages(self):
         from google.cloud.translate.client import ENGLISH_ISO_639
 
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         supported = [
             {'language': 'en', 'name': 'English'},
             {'language': 'af', 'name': 'Afrikaans'},
@@ -93,7 +72,7 @@ def test_get_languages(self):
         self.assertEqual(req['method'], 'GET')
         self.assertEqual(req['path'], '/languages')
         self.assertEqual(req['query_params'],
-                         {'key': self.KEY, 'target': ENGLISH_ISO_639})
+                         {'target': ENGLISH_ISO_639})
 
     def test_get_languages_no_target(self):
         client = self._make_one(
@@ -122,7 +101,7 @@ def test_get_languages_no_target(self):
         self.assertEqual(req['query_params'], {})
 
     def test_get_languages_explicit_target(self):
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         target_language = 'en'
         supported = [
             {'language': 'en', 'name': 'Spanish'},
@@ -145,10 +124,10 @@ def test_get_languages_explicit_target(self):
         self.assertEqual(req['method'], 'GET')
         self.assertEqual(req['path'], '/languages')
         self.assertEqual(req['query_params'],
-                         {'key': self.KEY, 'target': target_language})
+                         {'target': target_language})
 
     def test_detect_language_bad_result(self):
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         value = 'takoy'
         conn = client._connection = _Connection({})
 
@@ -161,13 +140,12 @@ def test_detect_language_bad_result(self):
         self.assertEqual(req['method'], 'GET')
         self.assertEqual(req['path'], '/detect')
         query_params = [
-            ('key', self.KEY),
             ('q', value.encode('utf-8')),
         ]
         self.assertEqual(req['query_params'], query_params)
 
     def test_detect_language_single_value(self):
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         value = 'takoy'
         detection = {
             'confidence': 1.0,
@@ -191,7 +169,6 @@ def test_detect_language_single_value(self):
         self.assertEqual(req['method'], 'GET')
         self.assertEqual(req['path'], '/detect')
         query_params = [
-            ('key', self.KEY),
             ('q', value.encode('utf-8')),
         ]
         self.assertEqual(req['query_params'], query_params)
@@ -237,7 +214,7 @@ def test_detect_language_multiple_values(self):
         self.assertEqual(req['query_params'], query_params)
 
     def test_detect_language_multiple_results(self):
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         value = 'soy'
         detection1 = {
             'confidence': 0.81496066,
@@ -262,7 +239,7 @@ def test_detect_language_multiple_results(self):
             client.detect_language(value)
 
     def test_translate_bad_result(self):
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         value = 'hvala ti'
         conn = client._connection = _Connection({})
 
@@ -276,7 +253,6 @@ def test_translate_bad_result(self):
         self.assertEqual(req['path'], '')
         query_params = [
             ('target', 'en'),
-            ('key', self.KEY),
             ('q', value.encode('utf-8')),
         ]
         self.assertEqual(req['query_params'], query_params)
@@ -311,7 +287,7 @@ def test_translate_defaults(self):
         self.assertEqual(req['query_params'], query_params)
 
     def test_translate_multiple(self):
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         value1 = 'hvala ti'
         translation1 = {
             'detectedSourceLanguage': 'hr',
@@ -341,14 +317,13 @@ def test_translate_multiple(self):
         self.assertEqual(req['path'], '')
         query_params = [
             ('target', 'en'),
-            ('key', self.KEY),
             ('q', value1.encode('utf-8')),
             ('q', value2.encode('utf-8')),
         ]
         self.assertEqual(req['query_params'], query_params)
 
     def test_translate_explicit(self):
-        client = self._make_one(api_key=self.KEY, http=object())
+        client = self._make_one(http=object())
         value = 'thank you'
         target_language = 'eo'
         source_language = 'en'
@@ -379,7 +354,6 @@ def test_translate_explicit(self):
         self.assertEqual(req['path'], '')
         query_params = [
             ('target', target_language),
-            ('key', self.KEY),
             ('q', value.encode('utf-8')),
             ('cid', cid),
             ('format', format_),