From 17dbc4e467a8d6ec46158610d930d9a92a661bd0 Mon Sep 17 00:00:00 2001 From: jonathanedey Date: Thu, 1 May 2025 18:17:40 -0400 Subject: [PATCH] fix: Set `x-goog-api-client` on request --- firebase_admin/_http_client.py | 4 ++-- firebase_admin/app_check.py | 2 +- firebase_admin/storage.py | 2 +- tests/test_auth_providers.py | 6 +++++- tests/test_db.py | 6 ++++-- tests/test_functions.py | 10 ++++++---- tests/test_http_client.py | 16 +++++++++++++++- tests/test_instance_id.py | 3 ++- tests/test_messaging.py | 6 ++++-- tests/test_ml.py | 3 ++- tests/test_project_management.py | 3 ++- tests/test_tenant_mgt.py | 18 ++++++++++++------ tests/test_user_mgt.py | 6 +++++- tests/testutils.py | 4 ++++ 14 files changed, 65 insertions(+), 24 deletions(-) diff --git a/firebase_admin/_http_client.py b/firebase_admin/_http_client.py index f1eccbcf..57c09e2e 100644 --- a/firebase_admin/_http_client.py +++ b/firebase_admin/_http_client.py @@ -38,7 +38,7 @@ DEFAULT_TIMEOUT_SECONDS = 120 METRICS_HEADERS = { - 'X-GOOG-API-CLIENT': _utils.get_metrics_header(), + 'x-goog-api-client': _utils.get_metrics_header(), } class HttpClient: @@ -76,7 +76,6 @@ def __init__( if headers: self._session.headers.update(headers) - self._session.headers.update(METRICS_HEADERS) if retries: self._session.mount('http://', requests.adapters.HTTPAdapter(max_retries=retries)) self._session.mount('https://', requests.adapters.HTTPAdapter(max_retries=retries)) @@ -120,6 +119,7 @@ class call this method to send HTTP requests out. Refer to """ if 'timeout' not in kwargs: kwargs['timeout'] = self.timeout + kwargs.setdefault('headers', {}).update(METRICS_HEADERS) resp = self._session.request(method, self.base_url + url, **kwargs) resp.raise_for_status() return resp diff --git a/firebase_admin/app_check.py b/firebase_admin/app_check.py index e6b66efc..53686db3 100644 --- a/firebase_admin/app_check.py +++ b/firebase_admin/app_check.py @@ -52,7 +52,7 @@ class _AppCheckService: _jwks_client = None _APP_CHECK_HEADERS = { - 'X-GOOG-API-CLIENT': _utils.get_metrics_header(), + 'x-goog-api-client': _utils.get_metrics_header(), } def __init__(self, app): diff --git a/firebase_admin/storage.py b/firebase_admin/storage.py index 46f5f604..b6084842 100644 --- a/firebase_admin/storage.py +++ b/firebase_admin/storage.py @@ -56,7 +56,7 @@ class _StorageClient: """Holds a Google Cloud Storage client instance.""" STORAGE_HEADERS = { - 'X-GOOG-API-CLIENT': _utils.get_metrics_header(), + 'x-goog-api-client': _utils.get_metrics_header(), } def __init__(self, credentials, project, default_bucket): diff --git a/tests/test_auth_providers.py b/tests/test_auth_providers.py index 48f38a01..304e0fd7 100644 --- a/tests/test_auth_providers.py +++ b/tests/test_auth_providers.py @@ -75,7 +75,11 @@ def _assert_request(request, expected_method, expected_url): assert request.method == expected_method assert request.url == expected_url assert request.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = [ + _utils.get_metrics_header(), + _utils.get_metrics_header() + ' mock-cred-metric-tag' + ] + assert request.headers['x-goog-api-client'] in expected_metrics_header class TestOIDCProviderConfig: diff --git a/tests/test_db.py b/tests/test_db.py index f2ba0882..00a0077c 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -198,7 +198,8 @@ def _assert_request(self, request, expected_method, expected_url): assert request.url == expected_url assert request.headers['Authorization'] == 'Bearer mock-token' assert request.headers['User-Agent'] == db._USER_AGENT - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert request.headers['x-goog-api-client'] == expected_metrics_header @pytest.mark.parametrize('data', valid_values) def test_get_value(self, data): @@ -665,7 +666,8 @@ def _assert_request(self, request, expected_method, expected_url): assert request.url == expected_url assert request.headers['Authorization'] == 'Bearer mock-token' assert request.headers['User-Agent'] == db._USER_AGENT - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert request.headers['x-goog-api-client'] == expected_metrics_header def test_get_value(self): ref = db.reference('/test') diff --git a/tests/test_functions.py b/tests/test_functions.py index f8f67589..1856426d 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -122,7 +122,8 @@ def test_task_enqueue(self): assert recorder[0].url == _DEFAULT_REQUEST_URL assert recorder[0].headers['Content-Type'] == 'application/json' assert recorder[0].headers['Authorization'] == 'Bearer mock-token' - assert recorder[0].headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert recorder[0].headers['x-goog-api-client'] == expected_metrics_header assert task_id == 'test-task-id' def test_task_enqueue_with_extension(self): @@ -139,7 +140,8 @@ def test_task_enqueue_with_extension(self): assert recorder[0].url == _CLOUD_TASKS_URL + resource_name assert recorder[0].headers['Content-Type'] == 'application/json' assert recorder[0].headers['Authorization'] == 'Bearer mock-token' - assert recorder[0].headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert recorder[0].headers['x-goog-api-client'] == expected_metrics_header assert task_id == 'test-task-id' def test_task_delete(self): @@ -149,8 +151,8 @@ def test_task_delete(self): assert len(recorder) == 1 assert recorder[0].method == 'DELETE' assert recorder[0].url == _DEFAULT_TASK_URL - assert recorder[0].headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() - + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert recorder[0].headers['x-goog-api-client'] == expected_metrics_header class TestTaskQueueOptions: diff --git a/tests/test_http_client.py b/tests/test_http_client.py index cc948b39..78036166 100644 --- a/tests/test_http_client.py +++ b/tests/test_http_client.py @@ -71,7 +71,21 @@ def test_metrics_headers(): assert len(recorder) == 1 assert recorder[0].method == 'GET' assert recorder[0].url == _TEST_URL - assert recorder[0].headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + assert recorder[0].headers['x-goog-api-client'] == _utils.get_metrics_header() + +def test_metrics_headers_with_credentials(): + client = _http_client.HttpClient( + credential=testutils.MockGoogleCredential()) + assert client.session is not None + recorder = _instrument(client, 'body') + resp = client.request('get', _TEST_URL) + assert resp.status_code == 200 + assert resp.text == 'body' + assert len(recorder) == 1 + assert recorder[0].method == 'GET' + assert recorder[0].url == _TEST_URL + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert recorder[0].headers['x-goog-api-client'] == expected_metrics_header def test_credential(): client = _http_client.HttpClient( diff --git a/tests/test_instance_id.py b/tests/test_instance_id.py index 720171cd..387e067c 100644 --- a/tests/test_instance_id.py +++ b/tests/test_instance_id.py @@ -68,7 +68,8 @@ def _instrument_iid_service(self, app, status=200, payload='True'): def _assert_request(self, request, expected_method, expected_url): assert request.method == expected_method assert request.url == expected_url - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert request.headers['x-goog-api-client'] == expected_metrics_header def _get_url(self, project_id, iid): return instance_id._IID_SERVICE_URL + 'project/{0}/instanceId/{1}'.format(project_id, iid) diff --git a/tests/test_messaging.py b/tests/test_messaging.py index b7b5c69b..45a5bc6d 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -1683,7 +1683,8 @@ def _assert_request(self, request, expected_method, expected_url, expected_body= assert request.url == expected_url assert request.headers['X-GOOG-API-FORMAT-VERSION'] == '2' assert request.headers['X-FIREBASE-CLIENT'] == self._CLIENT_VERSION - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert request.headers['x-goog-api-client'] == expected_metrics_header if expected_body is None: assert request.body is None else: @@ -2604,7 +2605,8 @@ def _assert_request(self, request, expected_method, expected_url): assert request.method == expected_method assert request.url == expected_url assert request.headers['access_token_auth'] == 'true' - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert request.headers['x-goog-api-client'] == expected_metrics_header def _get_url(self, path): return '{0}/{1}'.format(messaging._MessagingService.IID_URL, path) diff --git a/tests/test_ml.py b/tests/test_ml.py index 137fe4cf..18a9e275 100644 --- a/tests/test_ml.py +++ b/tests/test_ml.py @@ -339,7 +339,8 @@ def _assert_request(request, expected_method, expected_url): assert request.method == expected_method assert request.url == expected_url assert request.headers['X-FIREBASE-CLIENT'] == f'fire-admin-python/{firebase_admin.__version__}' - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert request.headers['x-goog-api-client'] == expected_metrics_header class _TestStorageClient: @staticmethod diff --git a/tests/test_project_management.py b/tests/test_project_management.py index 0a1bf97e..a242f523 100644 --- a/tests/test_project_management.py +++ b/tests/test_project_management.py @@ -523,7 +523,8 @@ def _assert_request_is_correct( assert request.method == expected_method assert request.url == expected_url assert request.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert request.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert request.headers['x-goog-api-client'] == expected_metrics_header if expected_body is None: assert request.body is None else: diff --git a/tests/test_tenant_mgt.py b/tests/test_tenant_mgt.py index 1da6d938..224fdcc1 100644 --- a/tests/test_tenant_mgt.py +++ b/tests/test_tenant_mgt.py @@ -197,7 +197,8 @@ def test_get_tenant(self, tenant_mgt_app): assert req.method == 'GET' assert req.url == '{0}/tenants/tenant-id'.format(TENANT_MGT_URL_PREFIX) assert req.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert req.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert req.headers['x-goog-api-client'] == expected_metrics_header def test_tenant_not_found(self, tenant_mgt_app): _instrument_tenant_mgt(tenant_mgt_app, 500, TENANT_NOT_FOUND_RESPONSE) @@ -289,7 +290,8 @@ def _assert_request(self, recorder, body): assert req.method == 'POST' assert req.url == '{0}/tenants'.format(TENANT_MGT_URL_PREFIX) assert req.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert req.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert req.headers['x-goog-api-client'] == expected_metrics_header got = json.loads(req.body.decode()) assert got == body @@ -389,7 +391,8 @@ def _assert_request(self, recorder, body, mask): assert req.url == '{0}/tenants/tenant-id?updateMask={1}'.format( TENANT_MGT_URL_PREFIX, ','.join(mask)) assert req.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert req.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert req.headers['x-goog-api-client'] == expected_metrics_header got = json.loads(req.body.decode()) assert got == body @@ -411,7 +414,8 @@ def test_delete_tenant(self, tenant_mgt_app): assert req.method == 'DELETE' assert req.url == '{0}/tenants/tenant-id'.format(TENANT_MGT_URL_PREFIX) assert req.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert req.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert req.headers['x-goog-api-client'] == expected_metrics_header def test_tenant_not_found(self, tenant_mgt_app): _instrument_tenant_mgt(tenant_mgt_app, 500, TENANT_NOT_FOUND_RESPONSE) @@ -555,7 +559,8 @@ def _assert_request(self, recorder, expected=None): req = recorder[0] assert req.method == 'GET' assert req.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert req.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert req.headers['x-goog-api-client'] == expected_metrics_header request = dict(parse.parse_qsl(parse.urlsplit(req.url).query)) assert request == expected @@ -932,7 +937,8 @@ def _assert_request( assert req.method == method assert req.url == '{0}/tenants/tenant-id{1}'.format(prefix, want_url) assert req.headers['X-Client-Version'] == f'Python/Admin/{firebase_admin.__version__}' - assert req.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = _utils.get_metrics_header() + ' mock-cred-metric-tag' + assert req.headers['x-goog-api-client'] == expected_metrics_header body = json.loads(req.body.decode()) assert body == want_body diff --git a/tests/test_user_mgt.py b/tests/test_user_mgt.py index 604ec995..34b698be 100644 --- a/tests/test_user_mgt.py +++ b/tests/test_user_mgt.py @@ -136,7 +136,11 @@ def _check_request(recorder, want_url, want_body=None, want_timeout=None): req = recorder[0] assert req.method == 'POST' assert req.url == '{0}{1}'.format(USER_MGT_URLS['PREFIX'], want_url) - assert req.headers['X-GOOG-API-CLIENT'] == _utils.get_metrics_header() + expected_metrics_header = [ + _utils.get_metrics_header(), + _utils.get_metrics_header() + ' mock-cred-metric-tag' + ] + assert req.headers['x-goog-api-client'] in expected_metrics_header if want_body: body = json.loads(req.body.decode()) assert body == want_body diff --git a/tests/testutils.py b/tests/testutils.py index 17013b46..62f7bd9b 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -123,6 +123,10 @@ def refresh(self, request): def service_account_email(self): return 'mock-email' + # Simulate x-goog-api-client modification in credential refresh + def _metric_header_for_usage(self): + return 'mock-cred-metric-tag' + class MockCredential(firebase_admin.credentials.Base): """A mock Firebase credential implementation."""