Skip to content

Fixing SSRC Typos and Minor Bugs #841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 46 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8727e91
feat(firestore): Add Firestore Multi Database Support (#818)
jonathanedey Oct 24, 2024
2a8170f
[chore] Bump cachecontrol (#819)
jonathanedey Oct 24, 2024
d8d6aea
chore: Create dependabot.yml (#820)
lahirumaramba Oct 24, 2024
32e8dd2
feat: Support passing `google.auth` typed credentials in `initialize_…
jonathanedey Nov 4, 2024
be56a0f
chore: Add `X-Goog-Api-Client` metric header to requests (#826)
jonathanedey Nov 4, 2024
50ace23
feat(firestore): Upgrade `google-cloud-firestore` to support Firestor…
jonathanedey Nov 7, 2024
d3e2a63
[chore] Release 6.6.0 (#829)
jonathanedey Nov 7, 2024
1b131f0
[chore] Release 6.6.0 Take 2 (#830)
jonathanedey Nov 7, 2024
e377491
Adding script for Python Bug Bash
Dec 4, 2024
f9f0635
Changes for percent comparison
Dec 5, 2024
5f6f03e
Fixing semantic version issues with invalid version
Dec 8, 2024
ba678a9
Config values must retrun default values from invalid get operations
Dec 8, 2024
84ebe4c
Fixing typos
Dec 18, 2024
12fb2f1
Adding requirements set is master branch
Dec 18, 2024
d276c3e
Updating tolerance for percentage evaluation
Dec 18, 2024
f22ef5b
Removing dependency changes from fix branch
Dec 18, 2024
43ab91e
chore: Skip integration test for deprecated FCM API and bump pypy CI …
jonathanedey Dec 19, 2024
8ba819a
chore: Adding delayed response message for holidays (#842)
jonathanedey Dec 20, 2024
00dff8c
Updating ServerConfig methods
Jan 5, 2025
a08b91b
Updating ServerConfig methods
Jan 5, 2025
0ce187f
Revert "chore: Adding delayed response message for holidays (#842)" (…
jonathanedey Jan 6, 2025
0788d22
Updating comments and vars for readability
Jan 7, 2025
2faf53f
feat(firestore): Add Firestore Multi Database Support (#818)
jonathanedey Oct 24, 2024
7ff94da
[chore] Bump cachecontrol (#819)
jonathanedey Oct 24, 2024
f0c504b
chore: Create dependabot.yml (#820)
lahirumaramba Oct 24, 2024
464f442
feat: Support passing `google.auth` typed credentials in `initialize_…
jonathanedey Nov 4, 2024
746ea96
chore: Add `X-Goog-Api-Client` metric header to requests (#826)
jonathanedey Nov 4, 2024
75d1746
feat(firestore): Upgrade `google-cloud-firestore` to support Firestor…
jonathanedey Nov 7, 2024
80f6fe3
[chore] Release 6.6.0 (#829)
jonathanedey Nov 7, 2024
6d8223c
[chore] Release 6.6.0 Take 2 (#830)
jonathanedey Nov 7, 2024
45c9ad6
chore: Skip integration test for deprecated FCM API and bump pypy CI …
jonathanedey Dec 19, 2024
61833d5
chore: Adding delayed response message for holidays (#842)
jonathanedey Dec 20, 2024
6088f28
Revert "chore: Adding delayed response message for holidays (#842)" (…
jonathanedey Jan 6, 2025
e940950
Implementation for Fetching and Caching Server Side Remote Config (#825)
pijushcs Nov 15, 2024
ccb186a
Implementation for Evaluating Condition and Custom Signals Server Sid…
rathovarun1032 Nov 15, 2024
473d31f
Adding script for Python Bug Bash
Dec 4, 2024
7264d31
Changes for percent comparison
Dec 5, 2024
43a6862
Fixing semantic version issues with invalid version
Dec 8, 2024
0f50a95
Config values must retrun default values from invalid get operations
Dec 8, 2024
4bae22c
Fixing typos
Dec 18, 2024
797611b
Updating tolerance for percentage evaluation
Dec 18, 2024
55990cf
Removing dependency changes from fix branch
Dec 18, 2024
b4b4c1f
Updating ServerConfig methods
Jan 5, 2025
ced6140
Updating ServerConfig methods
Jan 5, 2025
40d0672
Updating comments and vars for readability
Jan 7, 2025
19a8156
Merge branch 'ssrc-bug-bash' of github.com:firebase/firebase-admin-py…
Jan 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.8']
python: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy3.9']

steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ jobs:
uses: actions/[email protected]
with:
name: dist
path: dist

- name: Publish preflight check
id: preflight
Expand Down
2 changes: 1 addition & 1 deletion firebase_admin/__about__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

"""About information (version, etc) for Firebase Admin SDK."""

__version__ = '6.5.0'
__version__ = '6.6.0'
__title__ = 'firebase_admin'
__author__ = 'Firebase'
__license__ = 'Apache License 2.0'
Expand Down
8 changes: 6 additions & 2 deletions firebase_admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os
import threading

from google.auth.credentials import Credentials as GoogleAuthCredentials
from google.auth.exceptions import DefaultCredentialsError
from firebase_admin import credentials
from firebase_admin.__about__ import __version__
Expand Down Expand Up @@ -208,10 +209,13 @@ def __init__(self, name, credential, options):
'non-empty string.'.format(name))
self._name = name

if not isinstance(credential, credentials.Base):
if isinstance(credential, GoogleAuthCredentials):
self._credential = credentials._ExternalCredentials(credential) # pylint: disable=protected-access
elif isinstance(credential, credentials.Base):
self._credential = credential
else:
raise ValueError('Illegal Firebase credential provided. App must be initialized '
'with a valid credential instance.')
self._credential = credential
self._options = _AppOptions(options)
self._lock = threading.RLock()
self._services = {}
Expand Down
5 changes: 5 additions & 0 deletions firebase_admin/_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import requests
from requests.packages.urllib3.util import retry # pylint: disable=import-error

from firebase_admin import _utils

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

DEFAULT_TIMEOUT_SECONDS = 120

METRICS_HEADERS = {
'X-GOOG-API-CLIENT': _utils.get_metrics_header(),
}

class HttpClient:
"""Base HTTP client used to make HTTP calls.
Expand Down Expand Up @@ -72,6 +76,7 @@ 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))
Expand Down
3 changes: 3 additions & 0 deletions firebase_admin/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""Internal utilities common to all modules."""

import json
from platform import python_version

import google.auth
import requests
Expand Down Expand Up @@ -75,6 +76,8 @@
16: exceptions.UNAUTHENTICATED,
}

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

def _get_initialized_app(app):
"""Returns a reference to an initialized App instance."""
Expand Down
7 changes: 6 additions & 1 deletion firebase_admin/app_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class _AppCheckService:
_scoped_project_id = None
_jwks_client = None

_APP_CHECK_HEADERS = {
'X-GOOG-API-CLIENT': _utils.get_metrics_header(),
}

def __init__(self, app):
# Validate and store the project_id to validate the JWT claims
self._project_id = app.project_id
Expand All @@ -62,7 +66,8 @@ def __init__(self, app):
'GOOGLE_CLOUD_PROJECT environment variable.')
self._scoped_project_id = 'projects/' + app.project_id
# Default lifespan is 300 seconds (5 minutes) so we change it to 21600 seconds (6 hours).
self._jwks_client = PyJWKClient(self._JWKS_URL, lifespan=21600)
self._jwks_client = PyJWKClient(
self._JWKS_URL, lifespan=21600, headers=self._APP_CHECK_HEADERS)


def verify_token(self, token: str) -> Dict[str, Any]:
Expand Down
14 changes: 14 additions & 0 deletions firebase_admin/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import pathlib

import google.auth
from google.auth.credentials import Credentials as GoogleAuthCredentials
from google.auth.transport import requests
from google.oauth2 import credentials
from google.oauth2 import service_account
Expand Down Expand Up @@ -58,6 +59,19 @@ def get_credential(self):
"""Returns the Google credential instance used for authentication."""
raise NotImplementedError

class _ExternalCredentials(Base):
"""A wrapper for google.auth.credentials.Credentials typed credential instances"""

def __init__(self, credential: GoogleAuthCredentials):
super(_ExternalCredentials, self).__init__()
self._g_credential = credential

def get_credential(self):
"""Returns the underlying Google Credential

Returns:
google.auth.credentials.Credentials: A Google Auth credential instance."""
return self._g_credential

class Certificate(Base):
"""A credential initialized from a JSON certificate keyfile."""
Expand Down
88 changes: 52 additions & 36 deletions firebase_admin/firestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,75 @@
Firebase apps. This requires the ``google-cloud-firestore`` Python module.
"""

from __future__ import annotations
from typing import Optional, Dict
from firebase_admin import App
from firebase_admin import _utils

try:
from google.cloud import firestore # pylint: disable=import-error,no-name-in-module
from google.cloud import firestore
from google.cloud.firestore_v1.base_client import DEFAULT_DATABASE
existing = globals().keys()
for key, value in firestore.__dict__.items():
if not key.startswith('_') and key not in existing:
globals()[key] = value
except ImportError:
except ImportError as error:
raise ImportError('Failed to import the Cloud Firestore library for Python. Make sure '
'to install the "google-cloud-firestore" module.')

from firebase_admin import _utils
'to install the "google-cloud-firestore" module.') from error


_FIRESTORE_ATTRIBUTE = '_firestore'


def client(app=None) -> firestore.Client:
def client(app: Optional[App] = None, database_id: Optional[str] = None) -> firestore.Client:
"""Returns a client that can be used to interact with Google Cloud Firestore.

Args:
app: An App instance (optional).
app: An App instance (optional).
database_id: The database ID of the Google Cloud Firestore database to be used.
Defaults to the default Firestore database ID if not specified or an empty string
(optional).

Returns:
google.cloud.firestore.Firestore: A `Firestore Client`_.
google.cloud.firestore.Firestore: A `Firestore Client`_.

Raises:
ValueError: If a project ID is not specified either via options, credentials or
environment variables, or if the specified project ID is not a valid string.
ValueError: If the specified database ID is not a valid string, or if a project ID is not
specified either via options, credentials or environment variables, or if the specified
project ID is not a valid string.

.. _Firestore Client: https://googlecloudplatform.github.io/google-cloud-python/latest\
/firestore/client.html
.. _Firestore Client: https://cloud.google.com/python/docs/reference/firestore/latest/\
google.cloud.firestore_v1.client.Client
"""
fs_client = _utils.get_app_service(app, _FIRESTORE_ATTRIBUTE, _FirestoreClient.from_app)
return fs_client.get()


class _FirestoreClient:
"""Holds a Google Cloud Firestore client instance."""

def __init__(self, credentials, project):
self._client = firestore.Client(credentials=credentials, project=project)

def get(self):
return self._client

@classmethod
def from_app(cls, app):
"""Creates a new _FirestoreClient for the specified app."""
credentials = app.credential.get_credential()
project = app.project_id
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 GOOGLE_CLOUD_PROJECT '
'environment variable.')
return _FirestoreClient(credentials, project)
# Validate database_id
if database_id is not None and not isinstance(database_id, str):
raise ValueError(f'database_id "{database_id}" must be a string or None.')
fs_service = _utils.get_app_service(app, _FIRESTORE_ATTRIBUTE, _FirestoreService)
return fs_service.get_client(database_id)


class _FirestoreService:
"""Service that maintains a collection of firestore clients."""

def __init__(self, app: App) -> None:
self._app: App = app
self._clients: Dict[str, firestore.Client] = {}

def get_client(self, database_id: Optional[str]) -> firestore.Client:
"""Creates a client based on the database_id. These clients are cached."""
database_id = database_id or DEFAULT_DATABASE
if database_id not in self._clients:
# Create a new client and cache it in _clients
credentials = self._app.credential.get_credential()
project = self._app.project_id
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 '
'GOOGLE_CLOUD_PROJECT environment variable.')

fs_client = firestore.Client(
credentials=credentials, project=project, database=database_id)
self._clients[database_id] = fs_client

return self._clients[database_id]
94 changes: 52 additions & 42 deletions firebase_admin/firestore_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,65 +18,75 @@
associated with Firebase apps. This requires the ``google-cloud-firestore`` Python module.
"""

from typing import Type

from firebase_admin import (
App,
_utils,
)
from firebase_admin.credentials import Base
from __future__ import annotations
from typing import Optional, Dict
from firebase_admin import App
from firebase_admin import _utils

try:
from google.cloud import firestore # type: ignore # pylint: disable=import-error,no-name-in-module
from google.cloud import firestore
from google.cloud.firestore_v1.base_client import DEFAULT_DATABASE
existing = globals().keys()
for key, value in firestore.__dict__.items():
if not key.startswith('_') and key not in existing:
globals()[key] = value
except ImportError:
except ImportError as error:
raise ImportError('Failed to import the Cloud Firestore library for Python. Make sure '
'to install the "google-cloud-firestore" module.')
'to install the "google-cloud-firestore" module.') from error


_FIRESTORE_ASYNC_ATTRIBUTE: str = '_firestore_async'


def client(app: App = None) -> firestore.AsyncClient:
def client(app: Optional[App] = None, database_id: Optional[str] = None) -> firestore.AsyncClient:
"""Returns an async client that can be used to interact with Google Cloud Firestore.

Args:
app: An App instance (optional).
app: An App instance (optional).
database_id: The database ID of the Google Cloud Firestore database to be used.
Defaults to the default Firestore database ID if not specified or an empty string
(optional).

Returns:
google.cloud.firestore.Firestore_Async: A `Firestore Async Client`_.
google.cloud.firestore.Firestore_Async: A `Firestore Async Client`_.

Raises:
ValueError: If a project ID is not specified either via options, credentials or
environment variables, or if the specified project ID is not a valid string.
ValueError: If the specified database ID is not a valid string, or if a project ID is not
specified either via options, credentials or environment variables, or if the specified
project ID is not a valid string.

.. _Firestore Async Client: https://googleapis.dev/python/firestore/latest/client.html
.. _Firestore Async Client: https://cloud.google.com/python/docs/reference/firestore/latest/\
google.cloud.firestore_v1.async_client.AsyncClient
"""
fs_client = _utils.get_app_service(
app, _FIRESTORE_ASYNC_ATTRIBUTE, _FirestoreAsyncClient.from_app)
return fs_client.get()


class _FirestoreAsyncClient:
"""Holds a Google Cloud Firestore Async Client instance."""

def __init__(self, credentials: Type[Base], project: str) -> None:
self._client = firestore.AsyncClient(credentials=credentials, project=project)

def get(self) -> firestore.AsyncClient:
return self._client

@classmethod
def from_app(cls, app: App) -> "_FirestoreAsyncClient":
# Replace remove future reference quotes by importing annotations in Python 3.7+ b/238779406
"""Creates a new _FirestoreAsyncClient for the specified app."""
credentials = app.credential.get_credential()
project = app.project_id
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 GOOGLE_CLOUD_PROJECT '
'environment variable.')
return _FirestoreAsyncClient(credentials, project)
# Validate database_id
if database_id is not None and not isinstance(database_id, str):
raise ValueError(f'database_id "{database_id}" must be a string or None.')

fs_service = _utils.get_app_service(app, _FIRESTORE_ASYNC_ATTRIBUTE, _FirestoreAsyncService)
return fs_service.get_client(database_id)

class _FirestoreAsyncService:
"""Service that maintains a collection of firestore async clients."""

def __init__(self, app: App) -> None:
self._app: App = app
self._clients: Dict[str, firestore.AsyncClient] = {}

def get_client(self, database_id: Optional[str]) -> firestore.AsyncClient:
"""Creates an async client based on the database_id. These clients are cached."""
database_id = database_id or DEFAULT_DATABASE
if database_id not in self._clients:
# Create a new client and cache it in _clients
credentials = self._app.credential.get_credential()
project = self._app.project_id
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 '
'GOOGLE_CLOUD_PROJECT environment variable.')

fs_client = firestore.AsyncClient(
credentials=credentials, project=project, database=database_id)
self._clients[database_id] = fs_client

return self._clients[database_id]
Loading
Loading