8000 Fixing SSRC Typos and Minor Bugs (#841) · firebase/firebase-admin-python@065424a · GitHub
[go: up one dir, main page]

Skip to content

Commit 065424a

Browse files
pijushcsjonathanedeylahirumarambaPijush Chakrabortyrathovarun1032
authored
Fixing SSRC Typos and Minor Bugs (#841)
* Changes for percent comparison * Fixing semantic version issues with invalid version * Fixing Config values must retrun default values from invalid get operations * Updating tolerance for percentage evaluation * Removing dependency changes from fix branch * Updating ServerConfig methods as per review changes * Updating comments and vars for readability * Added unit and integration tests * Refactor and add unit test * Implementation for Fetching and Caching Server Side Remote Config (#825) * Minor update to API signature * Updating init params for ServerTemplateData * Adding validation errors and test * Removing parameter groups * Addressing PR comments and fixing async flow during fetch call * Fixing lint issues --------- Co-authored-by: Jonathan Edey <145066863+jonathanedey@users.noreply.github.com> Co-authored-by: Lahiru Maramba <llahiru@gmail.com> Co-authored-by: Pijush Chakraborty <pijushc@google.com> Co-authored-by: varun rathore <35365856+rathovarun1032@users.noreply.github.com> Co-authored-by: Varun Rathore <varunrathore@google.com>
1 parent 6f9591b commit 065424a

32 files changed

+826
-425
lines changed

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "pip"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
strategy:
99
fail-fast: false
1010
matrix:
11-
python: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.8']
11+
python: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.9']
1212

1313
steps:
1414
- uses: actions/checkout@v4

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ jobs:
108108
uses: actions/download-artifact@v4.1.7
109109
with:
110110
name: dist
111+
path: dist
111112

112113
- name: Publish preflight check
113114
id: preflight

firebase_admin/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

15< F438 /td>15
"""About information (version, etc) for Firebase Admin SDK."""
1616

17-
__version__ = '6.5.0'
17+
__version__ = '6.6.0'
1818
__title__ = 'firebase_admin'
1919
__author__ = 'Firebase'
2020
__license__ = 'Apache License 2.0'

firebase_admin/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import os
1919
import threading
2020

21+
from google.auth.credentials import Credentials as GoogleAuthCredentials
2122
from google.auth.exceptions import DefaultCredentialsError
2223
from firebase_admin import credentials
2324
from firebase_admin.__about__ import __version__
@@ -208,10 +209,13 @@ def __init__(self, name, credential, options):
208209
'non-empty string.'.format(name))
209210
self._name = name
210211

211-
if not isinstance(credential, credentials.Base):
212+
if isinstance(credential, GoogleAuthCredentials):
213+
self._credential = credentials._ExternalCredentials(credential) # pylint: disable=protected-access
214+
elif isinstance(credential, credentials.Base):
215+
self._credential = credential
216+
else:
212217
raise ValueError('Illegal Firebase credential provided. App must be initialized '
213218
'with a valid credential instance.')
214-
self._credential = credential
215219
self._options = _AppOptions(options)
216220
self._lock = threading.RLock()
217221
self._services = {}

firebase_admin/_http_client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import requests
2222
from requests.packages.urllib3.util import retry # pylint: disable=import-error
2323

24+
from firebase_admin import _utils
2425

2526
if hasattr(retry.Retry.DEFAULT, 'allowed_methods'):
2627
_ANY_METHOD = {'allowed_methods': None}
@@ -36,6 +37,9 @@
3637

3738
DEFAULT_TIMEOUT_SECONDS = 120
3839

40+
METRICS_HEADERS = {
41+
'X-GOOG-API-CLIENT': _utils.get_metrics_header(),
42+
}
3943

4044
class HttpClient:
4145
"""Base HTTP client used to make HTTP calls.
@@ -72,6 +76,7 @@ def __init__(
7276

7377
if headers:
7478
self._session.headers.update(headers)
79+
self._session.headers.update(METRICS_HEADERS)
7580
if retries:
7681
self._session.mount('http://', requests.adapters.HTTPAdapter(max_retries=retries))
7782
self._session.mount('https://', requests.adapters.HTTPAdapter(max_retries=retries))

firebase_admin/_utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Internal utilities common to all modules."""
1616

1717
import json
18+
from platform import python_version
1819

1920
import google.auth
2021
import requests
@@ -75,6 +76,8 @@
7576
16: exceptions.UNAUTHENTICATED,
7677
}
7778

79+
def get_metrics_header():
80+
return f'gl-python/{python_version()} fire-admin/{firebase_admin.__version__}'
7881

7982
def _get_initialized_app(app):
8083
"""Returns a reference to an initialized App instance."""

firebase_admin/app_check.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ class _AppCheckService:
5151
_scoped_project_id = None
5252
_jwks_client = None
5353

54+
_APP_CHECK_HEADERS = {
55+
'X-GOOG-API-CLIENT': _utils.get_metrics_header(),
56+
}
57+
5458
def __init__(self, app):
5559
# Validate and store the project_id to validate the JWT claims
5660
self._project_id = app.project_id
@@ -62,7 +66,8 @@ def __init__(self, app):
6266
'GOOGLE_CLOUD_PROJECT environment variable.')
6367
self._scoped_project_id = 'projects/' + app.project_id
6468
# Default lifespan is 300 seconds (5 minutes) so we change it to 21600 seconds (6 hours).
65-
self._jwks_client = PyJWKClient(self._JWKS_URL, lifespan=21600)
69+
self._jwks_client = PyJWKClient(
70+
self._JWKS_URL, lifespan=21600, headers=self._APP_CHECK_HEADERS)
6671

6772

6873
def verify_token(self, token: str) -> Dict[str, Any]:

firebase_admin/credentials.py

Lines changed: 14 additions & 0 deletions
< 1241 td data-grid-cell-id="diff-de674ec23c1821a24eca06014bbec7c7ff3574eeade29c7e4958dd7b260a59bc-60-63-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import pathlib
1919

2020
import google.auth
21+
from google.auth.credentials import Credentials as GoogleAuthCredentials
2122
from google.auth.transport import requests
2223
from google.oauth2 import credentials
2324
from google.oauth2 import service_account
@@ -58,6 +59,19 @@ def get_credential(self):
5859
"""Returns the Google credential instance used for authentication."""
5960
raise NotImplementedError
6061

62+
class _ExternalCredentials(Base):
63+
"""A wrapper for google.auth.credentials.Credentials typed credential instances"""
64+
65+
def __init__(self, credential: GoogleAuthCredentials):
66+
super(_ExternalCredentials, self).__init__()
67+
self._g_credential = credential
68+
69+
def get_credential(self):
70+
"""Returns the underlying Google Credential
71+
72+
Returns:
73+
google.auth.credentials.Credentials: A Google Auth credential instance."""
74+
return self._g_credential
6175

6276
class Certificate(Base):
6377
"""A credential initialized from a JSON certificate keyfile."""

firebase_admin/firestore.py

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,59 +18,75 @@
1818
Firebase apps. This requires the ``google-cloud-firestore`` Python module.
1919
"""
2020

21+
from __future__ import annotations
22+
from typing import Optional, Dict
23+
from firebase_admin import App
24+
from firebase_admin import _utils
25+
2126
try:
22-
from google.cloud import firestore # pylint: disable=import-error,no-name-in-module
27+
from google.cloud import firestore
28+
from google.cloud.firestore_v1.base_client import DEFAULT_DATABASE
2329
existing = globals().keys()
2430
for key, value in firestore.__dict__.items():
2531
if not key.startswith('_') and key not in existing:
2632
globals()[key] = value
27-
except ImportError:
33+
except ImportError as error:
2834
raise ImportError('Failed to import the Cloud Firestore library for Python. Make sure '
29-
'to install the "google-cloud-firestore" module.')
30-
31-
from firebase_admin import _utils
35+
'to install the "google-cloud-firestore" module.') from error
3236

3337

3438
_FIRESTORE_ATTRIBUTE = '_firestore'
3539

3640

37-
def client(app=None) -> firestore.Client:
41+
def client(app: Optional[App] = None, database_id: Optional[str] = None) -> firestore.Client:
3842
"""Returns a client that can be used to interact with Google Cloud Firestore.
3943
4044
Args:
41-
app: An App instance (optional).
45+
app: An App instance (optional).
46+
database_id: The database ID of the Google Cloud Firestore database to be used.
47+
Defaults to the default Firestore database ID if not specified or an empty string
48+
(optional).
4249
4350
Returns:
44-
google.cloud.firestore.Firestore: A `Firestore Client`_.
51+
google.cloud.firestore.Firestore: A `Firestore Client`_.
4552
4653
Raises:
47-
ValueError: If a project ID is not specified either via options, credentials or
48-
environment variables, or if the specified project ID is not a valid string.
54+
ValueError: If the specified database ID is not a valid string, or if a project ID is not
55+
specified either via options, credentials or environment variables, or if the specified
56+
project ID is not a valid string.
4957
50-
.. _Firestore Client: https://googlecloudplatform.github.io/google-cloud-python/latest\
51-
/firestore/client.html
58+
.. _Firestore Client: https://cloud.google.com/python/docs/reference/firestore/latest/\
59+
google.cloud.firestore_v1.client.Client
5260
"""
53-
fs_client = _utils.get_app_service(app, _FIRESTORE_ATTRIBUTE, _FirestoreClient.from_app)
54-
return fs_client.get()
55-
56-
57-
class _FirestoreClient:
58-
"""Holds a Google Cloud Firestore client instance."""
59-
60-
def __init__(self, credentials, project):
61-
self._client = firestore.Client(credentials=credentials, project=project)
62-
63-
def get(self):
64-
return self._client
65-
66-
@classmethod
67-
def from_app(cls, app):
68-
"""Creates a new _FirestoreClient for the specified app."""
69-
credentials = app.credential.get_credential()
70-
project = app.project_id
71-
if not project:
72-
raise ValueError(
73-
'Project ID is required to access Firestore. Either set the projectId option, '
74-
'or use service account credentials. Alternatively, set the GOOGLE_CLOUD_PROJECT '
75-
'environment variable.')
76-
return _FirestoreClient(credentials, project)
61+
# Validate database_id
62+
if database_id is not None and not isinstance(database_id, str):
63+
raise ValueError(f'database_id "{database_id}" must be a string or None.')
64+
fs_service = _utils.get_app_service(app, _FIRESTORE_ATTRIBUTE, _FirestoreService)
65+
return fs_service.get_client(database_id)
66+
67+
68+
class _FirestoreService:
69+
"""Service that maintains a collection of firestore clients."""
70+
71+
def __init__(self, app: App) -> None:
72+
self._app: App = app
73+
self._clients: Dict[str, firestore.Client] = {}
74+
75+
def get_client(self, database_id: Optional[str]) -> firestore.Client:
76+
"""Creates a client based on the database_id. These clients are cached."""
77+
database_id = database_id or DEFAULT_DATABASE
78+
if database_id not in self._clients:
79+
# Create a new client and cache it in _clients
80+
credentials = self._app.credential.get_credential()
81+
project = self._app.project_id
82+
if not project:
83+
raise ValueError(
84+
'Project ID is required to access Firestore. Either set the projectId option, '
85+
'or use service account credentials. Alternatively, set the '
86+
'GOOGLE_CLOUD_PROJECT environment variable.')
87+
88+
fs_client = firestore.Client(
89+
credentials=credentials, project=project, database=database_id)
90+
self._clients[database_id] = fs_client
91+
92+
return self._clients[database_id]

0 commit comments

Comments
 (0)
0