diff --git a/CHANGELOG.md b/CHANGELOG.md index dcde30bd4..bf25f8361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Unreleased -- +- [added] Admin SDK can now read the project ID from both `GCLOUD_PROJECT` and + `GOOGLE_CLOUD_PROJECT` environment variables. # v2.11.0 diff --git a/firebase_admin/__init__.py b/firebase_admin/__init__.py index 3b1c466c9..b3c802ec6 100644 --- a/firebase_admin/__init__.py +++ b/firebase_admin/__init__.py @@ -223,7 +223,8 @@ def _lookup_project_id(cls, credential, options): This method first inspects the app options for a ``projectId`` entry. Then it attempts to get the project ID from the credential used to initialize the app. If that also fails, - attempts to look up the ``GCLOUD_PROJECT`` environment variable. + attempts to look up the ``GOOGLE_CLOUD_PROJECT`` and ``GCLOUD_PROJECT`` environment + variables. Args: credential: A Firebase credential instance. @@ -235,18 +236,18 @@ def _lookup_project_id(cls, credential, options): Raises: ValueError: If a non-string project ID value is specified. """ - pid = options.get('projectId') - if not pid: + project_id = options.get('projectId') + if not project_id: try: - pid = credential.project_id + project_id = credential.project_id except AttributeError: pass - if not pid: - pid = os.environ.get('GCLOUD_PROJECT') - if pid is not None and not isinstance(pid, six.string_types): + if not project_id: + project_id = os.environ.get('GOOGLE_CLOUD_PROJECT', os.environ.get('GCLOUD_PROJECT')) + if project_id is not None and not isinstance(project_id, six.string_types): raise ValueError( - 'Invalid project ID: "{0}". project ID must be a string.'.format(pid)) - return pid + 'Invalid project ID: "{0}". project ID must be a string.'.format(project_id)) + return project_id @property def name(self): diff --git a/firebase_admin/_token_gen.py b/firebase_admin/_token_gen.py index e0118df76..1b2170cab 100644 --- a/firebase_admin/_token_gen.py +++ b/firebase_admin/_token_gen.py @@ -199,7 +199,7 @@ def verify(self, token, request): 'Failed to ascertain project ID from the credential or the environment. Project ' 'ID is required to call {0}. Initialize the app with a credentials.Certificate ' 'or set your Firebase project ID as an app option. Alternatively set the ' - 'GCLOUD_PROJECT environment variable.'.format(self.operation)) + 'GOOGLE_CLOUD_PROJECT environment variable.'.format(self.operation)) header = jwt.decode_header(token) payload = jwt.decode(token, verify=False) diff --git a/firebase_admin/firestore.py b/firebase_admin/firestore.py index 1c3236804..a9887b195 100644 --- a/firebase_admin/firestore.py +++ b/firebase_admin/firestore.py @@ -71,6 +71,6 @@ def from_app(cls, app): if not project: raise ValueError( 'Project ID is required to access Firestore. Either set the projectId option, ' - 'or use service account credentials. Alternatively, set the GCLOUD_PROJECT ' + 'or use service account credentials. Alternatively, set the GOOGLE_CLOUD_PROJECT ' 'environment variable.') return _FirestoreClient(credentials, project) diff --git a/firebase_admin/instance_id.py b/firebase_admin/instance_id.py index 70ace550f..11fb6b61a 100644 --- a/firebase_admin/instance_id.py +++ b/firebase_admin/instance_id.py @@ -78,7 +78,7 @@ def __init__(self, app): raise ValueError( 'Project ID is required to access Instance ID service. Either set the projectId ' 'option, or use service account credentials. Alternatively, set the ' - 'GCLOUD_PROJECT environment variable.') + 'GOOGLE_CLOUD_PROJECT environment variable.') self._project_id = project_id self._client = _http_client.JsonHttpClient( credential=app.credential.get_credential(), base_url=_IID_SERVICE_URL) diff --git a/firebase_admin/messaging.py b/firebase_admin/messaging.py index f7efafee0..e9e941c48 100644 --- a/firebase_admin/messaging.py +++ b/firebase_admin/messaging.py @@ -759,9 +759,9 @@ def __init__(self, app): project_id = app.project_id if not project_id: raise ValueError( - 'Project ID is required to access Cloud Messaging service. Either set the' - ' projectId option, or use service account credentials. Alternatively, set the ' - 'GCLOUD_PROJECT environment variable.') + 'Project ID is required to access Cloud Messaging service. Either set the ' + 'projectId option, or use service account credentials. Alternatively, set the ' + 'GOOGLE_CLOUD_PROJECT environment variable.') self._fcm_url = _MessagingService.FCM_URL.format(project_id) self._client = _http_client.JsonHttpClient(credential=app.credential.get_credential()) self._timeout = app.options.get('httpTimeout') diff --git a/tests/test_app.py b/tests/test_app.py index aaa3f0a26..9d3446692 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -25,7 +25,6 @@ CREDENTIAL = credentials.Certificate( testutils.resource_filename('service_account.json')) -GCLOUD_PROJECT = 'GCLOUD_PROJECT' CONFIG_JSON = firebase_admin._FIREBASE_CONFIG_ENV_VAR # This fixture will ignore the environment variable pointing to the default @@ -295,27 +294,26 @@ def test_project_id_from_credentials(self): assert app.project_id == 'mock-project-id' def test_project_id_from_environment(self): - project_id = os.environ.get(GCLOUD_PROJECT) - os.environ[GCLOUD_PROJECT] = 'env-project' - try: - app = firebase_admin.initialize_app(testutils.MockCredential(), name='myApp') - assert app.project_id == 'env-project' - finally: - if project_id: - os.environ[GCLOUD_PROJECT] = project_id - else: - del os.environ[GCLOUD_PROJECT] + variables = ['GOOGLE_CLOUD_PROJECT', 'GCLOUD_PROJECT'] + for idx, var in enumerate(variables): + old_project_id = os.environ.get(var) + new_project_id = 'env-project-{0}'.format(idx) + os.environ[var] = new_project_id + try: + app = firebase_admin.initialize_app( + testutils.MockCredential(), name='myApp{0}'.format(var)) + assert app.project_id == new_project_id + finally: + if old_project_id: + os.environ[var] = old_project_id + else: + del os.environ[var] def test_no_project_id(self): - project_id = os.environ.get(GCLOUD_PROJECT) - if project_id: - del os.environ[GCLOUD_PROJECT] - try: + def evaluate(): app = firebase_admin.initialize_app(testutils.MockCredential(), name='myApp') assert app.project_id is None - finally: - if project_id: - os.environ[GCLOUD_PROJECT] = project_id + testutils.run_without_project_id(evaluate) def test_non_string_project_id(self): options = {'projectId': {'key': 'not a string'}} diff --git a/tests/test_firestore.py b/tests/test_firestore.py index 5a17abd52..81fc492ac 100644 --- a/tests/test_firestore.py +++ b/tests/test_firestore.py @@ -14,8 +14,6 @@ """Tests for firebase_admin.firestore.""" -import os - import pytest import firebase_admin @@ -28,17 +26,11 @@ def teardown_function(): testutils.cleanup_apps() def test_no_project_id(): - env_var = 'GCLOUD_PROJECT' - gcloud_project = os.environ.get(env_var) - if gcloud_project: - del os.environ[env_var] - try: + def evaluate(): firebase_admin.initialize_app(testutils.MockCredential()) with pytest.raises(ValueError): firestore.client() - finally: - if gcloud_project: - os.environ[env_var] = gcloud_project + testutils.run_without_project_id(evaluate) def test_project_id(): cred = credentials.Certificate(testutils.resource_filename('service_account.json')) diff --git a/tests/test_instance_id.py b/tests/test_instance_id.py index 0f27f8ac9..e8e8edd27 100644 --- a/tests/test_instance_id.py +++ b/tests/test_instance_id.py @@ -14,8 +14,6 @@ """Tests for firebase_admin.instance_id.""" -import os - import pytest import firebase_admin @@ -47,17 +45,11 @@ def _get_url(self, project_id, iid): return instance_id._IID_SERVICE_URL + 'project/{0}/instanceId/{1}'.format(project_id, iid) def test_no_project_id(self): - env_var = 'GCLOUD_PROJECT' - gcloud_project = os.environ.get(env_var) - if gcloud_project: - del os.environ[env_var] - try: + def evaluate(): firebase_admin.initialize_app(testutils.MockCredential()) with pytest.raises(ValueError): instance_id.delete_instance_id('test') - finally: - if gcloud_project: - os.environ[env_var] = gcloud_project + testutils.run_without_project_id(evaluate) def test_delete_instance_id(self): cred = testutils.MockCredential() diff --git a/tests/test_messaging.py b/tests/test_messaging.py index a707ba1ad..a36f49c77 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -16,7 +16,6 @@ import datetime import json import numbers -import os import pytest import six @@ -871,17 +870,11 @@ def _get_url(self, project_id): return messaging._MessagingService.FCM_URL.format(project_id) def test_no_project_id(self): - env_var = 'GCLOUD_PROJECT' - gcloud_project = os.environ.get(env_var) - if gcloud_project: - del os.environ[env_var] - try: + def evaluate(): app = firebase_admin.initialize_app(testutils.MockCredential(), name='no_project_id') with pytest.raises(ValueError): messaging.send(messaging.Message(topic='foo'), app=app) - finally: - if gcloud_project: - os.environ[env_var] = gcloud_project + testutils.run_without_project_id(evaluate) @pytest.mark.parametrize('msg', NON_OBJECT_ARGS + [None]) def test_invalid_send(self, msg): diff --git a/tests/test_token_gen.py b/tests/test_token_gen.py index 93e399dc1..8a096afca 100644 --- a/tests/test_token_gen.py +++ b/tests/test_token_gen.py @@ -328,7 +328,10 @@ def test_project_id_option(self): finally: firebase_admin.delete_app(app) - @pytest.mark.parametrize('env_var_app', [{'GCLOUD_PROJECT': 'mock-project-id'}], indirect=True) + @pytest.mark.parametrize('env_var_app', [ + {'GCLOUD_PROJECT': 'mock-project-id'}, + {'GOOGLE_CLOUD_PROJECT': 'mock-project-id'} + ], indirect=True) def test_project_id_env_var(self, env_var_app): _overwrite_cert_request(env_var_app, MOCK_REQUEST) claims = auth.verify_id_token(TEST_ID_TOKEN, env_var_app) diff --git a/tests/testutils.py b/tests/testutils.py index 2be555895..56da01660 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -41,6 +41,22 @@ def cleanup_apps(): for app in apps: firebase_admin.delete_app(app) +def run_without_project_id(func): + env_vars = ['GCLOUD_PROJECT', 'GOOGLE_CLOUD_PROJECT'] + env_values = [] + for env_var in env_vars: + gcloud_project = os.environ.get(env_var) + if gcloud_project: + del os.environ[env_var] + env_values.append(gcloud_project) + try: + func() + finally: + for idx, env_var in enumerate(env_vars): + gcloud_project = env_values[idx] + if gcloud_project: + os.environ[env_var] = gcloud_project + class MockResponse(transport.Response): def __init__(self, status, response):