8000 Merge branch 'master' into clock_skew · firebase/firebase-admin-python@a13a683 · GitHub
[go: up one dir, main page]

Skip to content

Commit a13a683

Browse files
authored
Merge branch 'master' into clock_skew
2 parents 4d14005 + 336dbef commit a13a683

File tree

10 files changed

+444
-15
lines changed

10 files changed

+444
-15
lines changed

firebase_admin/__about__.py

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

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

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

firebase_admin/firestore_async.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright 2022 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Cloud Firestore Async module.
16+
17+
This module contains utilities for asynchronusly accessing the Google Cloud Firestore databases
18+
associated with Firebase apps. This requires the ``google-cloud-firestore`` Python module.
19+
"""
20+
21+
from typing import Type
22+
23+
from firebase_admin import (
24+
App,
25+
_utils,
26+
)
27+
from firebase_admin.credentials import Base
28+
29+
try:
30+
from google.cloud import firestore # type: ignore # pylint: disable=import-error,no-name-in-module
31+
existing = globals().keys()
32+
for key, value in firestore.__dict__.items():
33+
if not key.startswith('_') and key not in existing:
34+
globals()[key] = value
35+
except ImportError:
36+
raise ImportError('Failed to import the Cloud Firestore library for Python. Make sure '
37+
'to install the "google-cloud-firestore" module.')
38+
39+
_FIRESTORE_ASYNC_ATTRIBUTE: str = '_firestore_async'
40+
41+
42+
def client(app: App = None) -> firestore.AsyncClient:
43+
"""Returns an async client that can be used to interact with Google Cloud Firestore.
44+
45+
Args:
46+
app: An App instance (optional).
47+
48+
Returns:
49+
google.cloud.firestore.Firestore_Async: A `Firestore Async Client`_.
50+
51+
Raises:
52+
ValueError: If a project ID is not specified either via options, credentials or
53+
environment variables, or if the specified project ID is not a valid string.
54+
55+
.. _Firestore Async Client: https://googleapis.dev/python/firestore/latest/client.html
56+
"""
57+
fs_client = _utils.get_app_service(
58+
app, _FIRESTORE_ASYNC_ATTRIBUTE, _FirestoreAsyncClient.from_app)
59+
return fs_client.get()
60+
61+
62+
class _FirestoreAsyncClient:
63+
"""Holds a Google Cloud Firestore Async Client instance."""
64+
65+
def __init__(self, credentials: Type[Base], project: str) -> None:
66+
self._client = firestore.AsyncClient(credentials=credentials, project=project)
67+
68+
def get(self) -> firestore.AsyncClient:
69+
return self._client
70+
71+
@classmethod
72+
def from_app(cls, app: App) -> "_FirestoreAsyncClient":
73+
# Replace remove future reference quotes by importing annotations in Python 3.7+ b/238779406
74+
"""Creates a new _FirestoreAsyncClient for the specified app."""
75+
credentials = app.credential.get_credential()
76+
project = app.project_id
77+
if not project:
78+
raise ValueError(
79+
'Project ID is required to access Firestore. Either set the projectId option, '
80+
'or use service account credentials. Alternatively, set the GOOGLE_CLOUD_PROJECT '
81+
'environment variable.')
82+
return _FirestoreAsyncClient(credentials, project)

integration/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""pytest configuration and global fixtures for integration tests."""
1616
import json
1717

18+
import asyncio
1819
import pytest
1920

2021
import firebase_admin
@@ -70,3 +71,12 @@ def api_key(request):
7071
'command-line option.')
7172
with open(path) as keyfile:
7273
return keyfile.read().strip()
74+
75+
@pytest.fixture(scope="session")
76+
def event_loop():
77+
"""Create an instance of the default event loop for test session.
78+
This avoids early eventloop closure.
79+
"""
80+
loop = asyncio.get_event_loop_policy().new_event_loop()
81+
yield loop
82+
loop.close()

integration/test_firestore_async.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright 2022 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Integration tests for firebase_admin.firestore_async module."""
16+
import datetime
17+
import pytest
18+
19+
from firebase_admin import firestore_async
20+
21+
@pytest.mark.asyncio
22+
async def test_firestore_async():
23+
client = firestore_async.client()
24+
expected = {
25+
'name': u'Mountain View',
26+
'country': u'USA',
27+
'population': 77846,
28+
'capital': False
29+
}
30+
doc = client.collection('cities').document()
31+
await doc.set(expected)
32+
33+
data = await doc.get()
34+
assert data.to_dict() == expected
35+
36+
await doc.delete()
37+
data = await doc.get()
38+
assert data.exists is False
39+
40+
@pytest.mark.asyncio
41+
async def test_server_timestamp():
42+
client = firestore_async.client()
43+
expected = {
44+
'name': u'Mountain View',
45+
'timestamp': firestore_async.SERVER_TIMESTAMP # pylint: disable=no-member
46+
}
47+
doc = client.collection('cities').document()
48+
await doc.set(expected)
49+
50+
data = await doc.get()
51+
data = data.to_dict()
52+
assert isinstance(data['timestamp'], datetime.datetime)
53+
await doc.delete()

integration/test_project_management.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,26 +128,12 @@ def test_android_sha_certificates(android_app):
128128
for cert in cert_list:
129129
assert cert.name
130130

131-
# Adding the same cert twice should cause an already-exists error.
132-
with pytest.raises(exceptions.AlreadyExistsError) as excinfo:
133-
android_app.add_sha_certificate(project_management.SHACertificate(SHA_256_HASH_2))
134-
assert 'Requested entity already exists' in str(excinfo.value)
135-
assert excinfo.value.cause is not None
136-
assert excinfo.value.http_response is not None
137-
138131
# Delete all certs and assert that they have all been deleted successfully.
139132
for cert in cert_list:
140133
android_app.delete_sha_certificate(cert)
141134

142135
assert android_app.get_sha_certificates() == []
143136

144-
# Deleting a nonexistent cert should cause a not-found error.
145-
with pytest.raises(exceptions.NotFoundError) as excinfo:
146-
android_app.delete_sha_certificate(cert_list[0])
147-
assert 'Requested entity was not found' in str(excinfo.value)
148-
assert excinfo.value.cause is not None
149-
assert excinfo.value.http_response is not None
150-
151137

152138
def test_create_ios_app_already_exists(ios_app):
153139
del ios_app

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pylint == 2.3.1
33
pytest >= 6.2.0
44
pytest-cov >= 2.4.0
55
pytest-localserver >= 0.4.1
6+
pytest-asyncio >= 0.16.0
67

78
cachecontrol >= 0.12.6
89
google-api-core[grpc] >= 1.22.1, < 3.0.0dev; platform.python_implementation != 'PyPy'

snippets/firestore/__init__.py

Whitespace-only changes.

snippets/firestore/firestore.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright 2022 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from firebase_admin import firestore
16+
17+
# pylint: disable=invalid-name
18+
def init_firestore_client():
19+
# [START init_firestore_client]
20+
import firebase_admin
21+
from firebase_admin import firestore
22+
23+
# Application Default credentials are automatically created.
24+
app = firebase_admin.initialize_app()
25+
db = firestore.client()
26+
# [END init_firestore_client]
27+
28+
def init_firestore_client_application_default():
29+
# [START init_firestore_client_application_default]
30+
import firebase_admin
31+
from firebase_admin import credentials
32+
from firebase_admin import firestore
33+
34+
# Use the application default credentials.
35+
cred = credentials.ApplicationDefault()
36+
37+
firebase_admin.initialize_app(cred)
38+
db = firestore.client()
39+
# [END init_firestore_client_application_default]
40+
41+
def init_firestore_client_service_account():
42+
# [START init_firestore_client_service_account]
43+
import firebase_admin
44+
from firebase_admin import credentials
45+
from firebase_admin import firestore
46+
47+
# Use a service account.
48+
cred = credentials.Certificate('path/to/serviceAccount.json')
49+
50+
app = firebase_admin.initialize_app(cred)
51+
52+
db = firestore.client()
53+
# [END init_firestore_client_service_account]
54+
55+
def read_data():
56+
import firebase_admin
57+
from firebase_admin import firestore
58+
59+
app = firebase_admin.initialize_app()
60+
db = firestore.client()
61+
62+
# [START read_data]
63+
doc_ref = db.collection('users').document('alovelace')
64+
doc = doc_ref.get()
65+
if doc.exists:
66+
return f'data: {doc.to_dict()}'
67+
return "Document does not exist."
68+
# [END read_data]
69+
70+
def add_data():
71+
import firebase_admin
72+
from firebase_admin import firestore
73+
74+
app = firebase_admin.initialize_app()
75+
db = firestore.client()
76+
77+
# [START add_data]
78+
doc_ref = db.collection("users").document("alovelace")
79+
doc_ref.set({
80+
"first": "Ada",
81+
"last": "Lovelace",
82+
"born": 1815
83+
})
84+
# [END add_data]

0 commit comments

Comments
 (0)
0