firebase-functions 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/PKG-INFO +1 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/setup.py +2 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/__init__.py +1 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/alerts_fn.py +2 -2
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/core.py +45 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/db_fn.py +1 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/eventarc_fn.py +2 -2
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/firestore_fn.py +257 -12
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/https_fn.py +20 -9
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/logger.py +3 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/options.py +15 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/params.py +44 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/_alerts_fn.py +13 -2
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/_identity_fn.py +5 -4
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/manifest.py +8 -3
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/path_pattern.py +4 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/util.py +51 -13
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/pubsub_fn.py +2 -2
- firebase_functions-0.4.0/src/firebase_functions/py.typed +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/remote_config_fn.py +8 -2
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/scheduler_fn.py +2 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/storage_fn.py +2 -2
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/tasks_fn.py +4 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/test_lab_fn.py +8 -2
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions.egg-info/PKG-INFO +1 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions.egg-info/SOURCES.txt +7 -0
- firebase_functions-0.4.0/tests/test_db.py +43 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_eventarc_fn.py +34 -0
- firebase_functions-0.4.0/tests/test_firestore_fn.py +123 -0
- firebase_functions-0.4.0/tests/test_https_fn.py +72 -0
- firebase_functions-0.4.0/tests/test_identity_fn.py +64 -0
- firebase_functions-0.4.0/tests/test_init.py +23 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_manifest.py +25 -27
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_params.py +36 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_pubsub_fn.py +35 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_scheduler_fn.py +19 -1
- firebase_functions-0.4.0/tests/test_storage_fn.py +43 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_tasks_fn.py +65 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_test_lab_fn.py +47 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_util.py +9 -1
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/LICENSE +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/README.md +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/pyproject.toml +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/setup.cfg +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/alerts/__init__.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/alerts/app_distribution_fn.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/alerts/billing_fn.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/alerts/crashlytics_fn.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/alerts/performance_fn.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/identity_fn.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/__init__.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/serving.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/private/token_verifier.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions.egg-info/dependency_links.txt +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions.egg-info/requires.txt +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions.egg-info/top_level.txt +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_logger.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_options.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_path_pattern.py +0 -0
- {firebase_functions-0.2.0 → firebase_functions-0.4.0}/tests/test_remote_config_fn.py +0 -0
|
@@ -56,6 +56,8 @@ setup(
|
|
|
56
56
|
extras_require={'dev': dev_requires},
|
|
57
57
|
packages=find_packages(where='src'),
|
|
58
58
|
package_dir={'': 'src'},
|
|
59
|
+
include_package_data=True,
|
|
60
|
+
package_data={'firebase_functions': ['py.typed']},
|
|
59
61
|
python_requires='>=3.10',
|
|
60
62
|
classifiers=[
|
|
61
63
|
'Development Status :: 4 - Beta',
|
|
@@ -24,7 +24,7 @@ from firebase_functions.alerts import FirebaseAlertData
|
|
|
24
24
|
|
|
25
25
|
import firebase_functions.private.util as _util
|
|
26
26
|
|
|
27
|
-
from firebase_functions.core import T, CloudEvent as _CloudEvent
|
|
27
|
+
from firebase_functions.core import T, CloudEvent as _CloudEvent, _with_init
|
|
28
28
|
from firebase_functions.options import FirebaseAlertOptions
|
|
29
29
|
|
|
30
30
|
# Explicitly import AlertType to make it available in the public API.
|
|
@@ -95,7 +95,7 @@ def on_alert_published(
|
|
|
95
95
|
@_functools.wraps(func)
|
|
96
96
|
def on_alert_published_wrapped(raw: _ce.CloudEvent):
|
|
97
97
|
from firebase_functions.private._alerts_fn import alerts_event_from_ce
|
|
98
|
-
func(alerts_event_from_ce(raw))
|
|
98
|
+
_with_init(func)(alerts_event_from_ce(raw))
|
|
99
99
|
|
|
100
100
|
_util.set_func_endpoint_attr(
|
|
101
101
|
on_alert_published_wrapped,
|
|
@@ -18,6 +18,8 @@ import dataclasses as _dataclass
|
|
|
18
18
|
import datetime as _datetime
|
|
19
19
|
import typing as _typing
|
|
20
20
|
|
|
21
|
+
from . import logger as _logger
|
|
22
|
+
|
|
21
23
|
T = _typing.TypeVar("T")
|
|
22
24
|
|
|
23
25
|
|
|
@@ -80,3 +82,46 @@ class Change(_typing.Generic[T]):
|
|
|
80
82
|
"""
|
|
81
83
|
The state of data after the change.
|
|
82
84
|
"""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
_did_init = False
|
|
88
|
+
_init_callback: _typing.Callable[[], _typing.Any] | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def init(callback: _typing.Callable[[], _typing.Any]) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Registers a function that should be run when in a production environment
|
|
94
|
+
before executing any functions code.
|
|
95
|
+
Calling this decorator more than once leads to undefined behavior.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
global _did_init
|
|
99
|
+
global _init_callback
|
|
100
|
+
|
|
101
|
+
if _did_init:
|
|
102
|
+
_logger.warn(
|
|
103
|
+
"Setting init callback more than once. Only the most recent callback will be called"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
_init_callback = callback
|
|
107
|
+
_did_init = False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _with_init(
|
|
111
|
+
fn: _typing.Callable[...,
|
|
112
|
+
_typing.Any]) -> _typing.Callable[..., _typing.Any]:
|
|
113
|
+
"""
|
|
114
|
+
A decorator that runs the init callback before running the decorated function.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def wrapper(*args, **kwargs):
|
|
118
|
+
global _did_init
|
|
119
|
+
|
|
120
|
+
if not _did_init:
|
|
121
|
+
if _init_callback is not None:
|
|
122
|
+
_init_callback()
|
|
123
|
+
_did_init = True
|
|
124
|
+
|
|
125
|
+
return fn(*args, **kwargs)
|
|
126
|
+
|
|
127
|
+
return wrapper
|
|
@@ -21,7 +21,7 @@ import cloudevents.http as _ce
|
|
|
21
21
|
|
|
22
22
|
import firebase_functions.options as _options
|
|
23
23
|
import firebase_functions.private.util as _util
|
|
24
|
-
from firebase_functions.core import CloudEvent
|
|
24
|
+
from firebase_functions.core import CloudEvent, _with_init
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@_util.copy_func_kwargs(_options.EventarcTriggerOptions)
|
|
@@ -73,7 +73,7 @@ def on_custom_event_published(
|
|
|
73
73
|
),
|
|
74
74
|
type=event_dict["type"],
|
|
75
75
|
)
|
|
76
|
-
func(event)
|
|
76
|
+
_with_init(func)(event)
|
|
77
77
|
|
|
78
78
|
_util.set_func_endpoint_attr(
|
|
79
79
|
on_custom_event_published_wrapped,
|
{firebase_functions-0.2.0 → firebase_functions-0.4.0}/src/firebase_functions/firestore_fn.py
RENAMED
|
@@ -38,6 +38,11 @@ _event_type_created = "google.cloud.firestore.document.v1.created"
|
|
|
38
38
|
_event_type_updated = "google.cloud.firestore.document.v1.updated"
|
|
39
39
|
_event_type_deleted = "google.cloud.firestore.document.v1.deleted"
|
|
40
40
|
|
|
41
|
+
_event_type_written_with_auth_context = "google.cloud.firestore.document.v1.written.withAuthContext"
|
|
42
|
+
_event_type_created_with_auth_context = "google.cloud.firestore.document.v1.created.withAuthContext"
|
|
43
|
+
_event_type_updated_with_auth_context = "google.cloud.firestore.document.v1.updated.withAuthContext"
|
|
44
|
+
_event_type_deleted_with_auth_context = "google.cloud.firestore.document.v1.deleted.withAuthContext"
|
|
45
|
+
|
|
41
46
|
|
|
42
47
|
@_dataclass.dataclass(frozen=True)
|
|
43
48
|
class Event(_core.CloudEvent[_core.T]):
|
|
@@ -82,9 +87,26 @@ _E2 = Event[DocumentSnapshot | None]
|
|
|
82
87
|
_C1 = _typing.Callable[[_E1], None]
|
|
83
88
|
_C2 = _typing.Callable[[_E2], None]
|
|
84
89
|
|
|
90
|
+
AuthType = _typing.Literal["service_account", "api_key", "system",
|
|
91
|
+
"unauthenticated", "unknown"]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@_dataclass.dataclass(frozen=True)
|
|
95
|
+
class AuthEvent(Event[_core.T]):
|
|
96
|
+
auth_type: AuthType
|
|
97
|
+
"""The type of principal that triggered the event"""
|
|
98
|
+
auth_id: str | None
|
|
99
|
+
"""The unique identifier for the principal"""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
_E3 = AuthEvent[Change[DocumentSnapshot | None]]
|
|
103
|
+
_E4 = AuthEvent[DocumentSnapshot | None]
|
|
104
|
+
_C3 = _typing.Callable[[_E3], None]
|
|
105
|
+
_C4 = _typing.Callable[[_E4], None]
|
|
106
|
+
|
|
85
107
|
|
|
86
108
|
def _firestore_endpoint_handler(
|
|
87
|
-
func: _C1 | _C2,
|
|
109
|
+
func: _C1 | _C2 | _C3 | _C4,
|
|
88
110
|
event_type: str,
|
|
89
111
|
document_pattern: _path_pattern.PathPattern,
|
|
90
112
|
raw: _ce.CloudEvent,
|
|
@@ -94,12 +116,14 @@ def _firestore_endpoint_handler(
|
|
|
94
116
|
firestore_event_data: _firestore.DocumentEventData
|
|
95
117
|
content_type: str = event_attributes["datacontenttype"]
|
|
96
118
|
if "application/json" in content_type or isinstance(event_data, dict):
|
|
97
|
-
firestore_event_data =
|
|
98
|
-
|
|
119
|
+
firestore_event_data = _typing.cast(
|
|
120
|
+
_firestore.DocumentEventData,
|
|
121
|
+
_firestore.DocumentEventData.from_json(event_data))
|
|
99
122
|
elif "application/protobuf" in content_type or isinstance(
|
|
100
123
|
event_data, bytes):
|
|
101
|
-
firestore_event_data =
|
|
102
|
-
|
|
124
|
+
firestore_event_data = _typing.cast(
|
|
125
|
+
_firestore.DocumentEventData,
|
|
126
|
+
_firestore.DocumentEventData.deserialize(event_data))
|
|
103
127
|
else:
|
|
104
128
|
actual_type = type(event_data)
|
|
105
129
|
raise TypeError(f"Firestore: Cannot parse event payload of data type "
|
|
@@ -110,6 +134,8 @@ def _firestore_endpoint_handler(
|
|
|
110
134
|
event_namespace = event_attributes["namespace"]
|
|
111
135
|
event_document = event_attributes["document"]
|
|
112
136
|
event_database = event_attributes["database"]
|
|
137
|
+
event_auth_type = event_attributes["authtype"]
|
|
138
|
+
event_auth_id = event_attributes["authid"]
|
|
113
139
|
|
|
114
140
|
time = event_attributes["time"]
|
|
115
141
|
event_time = _util.timestamp_conversion(time)
|
|
@@ -146,18 +172,23 @@ def _firestore_endpoint_handler(
|
|
|
146
172
|
firestore_event_data.old_value.update_time,
|
|
147
173
|
)
|
|
148
174
|
if event_type == _event_type_deleted:
|
|
149
|
-
firestore_event_data =
|
|
175
|
+
firestore_event_data = _typing.cast(_firestore.DocumentEventData,
|
|
176
|
+
old_value_snapshot)
|
|
150
177
|
if event_type == _event_type_created:
|
|
151
|
-
firestore_event_data =
|
|
178
|
+
firestore_event_data = _typing.cast(_firestore.DocumentEventData,
|
|
179
|
+
value_snapshot)
|
|
152
180
|
if event_type in (_event_type_written, _event_type_updated):
|
|
153
|
-
firestore_event_data =
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
181
|
+
firestore_event_data = _typing.cast(
|
|
182
|
+
_firestore.DocumentEventData,
|
|
183
|
+
Change(
|
|
184
|
+
before=old_value_snapshot,
|
|
185
|
+
after=value_snapshot,
|
|
186
|
+
))
|
|
157
187
|
|
|
158
188
|
params: dict[str, str] = {
|
|
159
189
|
**document_pattern.extract_matches(event_document),
|
|
160
190
|
}
|
|
191
|
+
|
|
161
192
|
database_event = Event(
|
|
162
193
|
project=event_project,
|
|
163
194
|
namespace=event_namespace,
|
|
@@ -173,7 +204,17 @@ def _firestore_endpoint_handler(
|
|
|
173
204
|
subject=event_attributes["subject"],
|
|
174
205
|
params=params,
|
|
175
206
|
)
|
|
176
|
-
|
|
207
|
+
|
|
208
|
+
func = _core._with_init(func)
|
|
209
|
+
|
|
210
|
+
if event_type.endswith(".withAuthContext"):
|
|
211
|
+
database_event_with_auth_context = AuthEvent(**vars(database_event),
|
|
212
|
+
auth_type=event_auth_type,
|
|
213
|
+
auth_id=event_auth_id)
|
|
214
|
+
func(database_event_with_auth_context)
|
|
215
|
+
else:
|
|
216
|
+
# mypy cannot infer that the event type is correct, hence the cast
|
|
217
|
+
_typing.cast(_C1 | _C2, func)(database_event)
|
|
177
218
|
|
|
178
219
|
|
|
179
220
|
@_util.copy_func_kwargs(FirestoreOptions)
|
|
@@ -224,6 +265,57 @@ def on_document_written(**kwargs) -> _typing.Callable[[_C1], _C1]:
|
|
|
224
265
|
return on_document_written_inner_decorator
|
|
225
266
|
|
|
226
267
|
|
|
268
|
+
@_util.copy_func_kwargs(FirestoreOptions)
|
|
269
|
+
def on_document_written_with_auth_context(**kwargs
|
|
270
|
+
) -> _typing.Callable[[_C1], _C1]:
|
|
271
|
+
"""
|
|
272
|
+
Event handler that triggers when a document is created, updated, or deleted in Firestore.
|
|
273
|
+
This trigger will also provide the authentication context of the principal who triggered
|
|
274
|
+
the event.
|
|
275
|
+
|
|
276
|
+
Example:
|
|
277
|
+
|
|
278
|
+
.. code-block:: python
|
|
279
|
+
|
|
280
|
+
@on_document_written_with_auth_context(document="*")
|
|
281
|
+
def example(event: AuthEvent[Change[DocumentSnapshot]]) -> None:
|
|
282
|
+
pass
|
|
283
|
+
|
|
284
|
+
:param \\*\\*kwargs: Firestore options.
|
|
285
|
+
:type \\*\\*kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
|
|
286
|
+
:rtype: :exc:`typing.Callable`
|
|
287
|
+
\\[ \\[ :exc:`firebase_functions.firestore_fn.AuthEvent` \\[
|
|
288
|
+
:exc:`firebase_functions.db.Change` \\] \\], `None` \\]
|
|
289
|
+
A function that takes a Firestore event and returns ``None``.
|
|
290
|
+
"""
|
|
291
|
+
options = FirestoreOptions(**kwargs)
|
|
292
|
+
|
|
293
|
+
def on_document_written_with_auth_context_inner_decorator(func: _C1):
|
|
294
|
+
document_pattern = _path_pattern.PathPattern(
|
|
295
|
+
_util.normalize_path(options.document))
|
|
296
|
+
|
|
297
|
+
@_functools.wraps(func)
|
|
298
|
+
def on_document_written_with_auth_context_wrapped(raw: _ce.CloudEvent):
|
|
299
|
+
return _firestore_endpoint_handler(
|
|
300
|
+
func,
|
|
301
|
+
_event_type_written_with_auth_context,
|
|
302
|
+
document_pattern,
|
|
303
|
+
raw,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
_util.set_func_endpoint_attr(
|
|
307
|
+
on_document_written_with_auth_context_wrapped,
|
|
308
|
+
options._endpoint(
|
|
309
|
+
event_type=_event_type_written,
|
|
310
|
+
func_name=func.__name__,
|
|
311
|
+
document_pattern=document_pattern,
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
return on_document_written_with_auth_context_wrapped
|
|
315
|
+
|
|
316
|
+
return on_document_written_with_auth_context_inner_decorator
|
|
317
|
+
|
|
318
|
+
|
|
227
319
|
@_util.copy_func_kwargs(FirestoreOptions)
|
|
228
320
|
def on_document_updated(**kwargs) -> _typing.Callable[[_C1], _C1]:
|
|
229
321
|
"""
|
|
@@ -272,6 +364,57 @@ def on_document_updated(**kwargs) -> _typing.Callable[[_C1], _C1]:
|
|
|
272
364
|
return on_document_updated_inner_decorator
|
|
273
365
|
|
|
274
366
|
|
|
367
|
+
@_util.copy_func_kwargs(FirestoreOptions)
|
|
368
|
+
def on_document_updated_with_auth_context(**kwargs
|
|
369
|
+
) -> _typing.Callable[[_C1], _C1]:
|
|
370
|
+
"""
|
|
371
|
+
Event handler that triggers when a document is updated in Firestore.
|
|
372
|
+
This trigger will also provide the authentication context of the principal who triggered
|
|
373
|
+
the event.
|
|
374
|
+
|
|
375
|
+
Example:
|
|
376
|
+
|
|
377
|
+
.. code-block:: python
|
|
378
|
+
|
|
379
|
+
@on_document_updated_with_auth_context(document="*")
|
|
380
|
+
def example(event: AuthEvent[Change[DocumentSnapshot]]) -> None:
|
|
381
|
+
pass
|
|
382
|
+
|
|
383
|
+
:param \\*\\*kwargs: Firestore options.
|
|
384
|
+
:type \\*\\*kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
|
|
385
|
+
:rtype: :exc:`typing.Callable`
|
|
386
|
+
\\[ \\[ :exc:`firebase_functions.firestore_fn.AuthEvent` \\[
|
|
387
|
+
:exc:`firebase_functions.db.Change` \\] \\], `None` \\]
|
|
388
|
+
A function that takes a Firestore event and returns ``None``.
|
|
389
|
+
"""
|
|
390
|
+
options = FirestoreOptions(**kwargs)
|
|
391
|
+
|
|
392
|
+
def on_document_updated_with_auth_context_inner_decorator(func: _C1):
|
|
393
|
+
document_pattern = _path_pattern.PathPattern(
|
|
394
|
+
_util.normalize_path(options.document))
|
|
395
|
+
|
|
396
|
+
@_functools.wraps(func)
|
|
397
|
+
def on_document_updated_with_auth_context_wrapped(raw: _ce.CloudEvent):
|
|
398
|
+
return _firestore_endpoint_handler(
|
|
399
|
+
func,
|
|
400
|
+
_event_type_updated_with_auth_context,
|
|
401
|
+
document_pattern,
|
|
402
|
+
raw,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
_util.set_func_endpoint_attr(
|
|
406
|
+
on_document_updated_with_auth_context_wrapped,
|
|
407
|
+
options._endpoint(
|
|
408
|
+
event_type=_event_type_updated_with_auth_context,
|
|
409
|
+
func_name=func.__name__,
|
|
410
|
+
document_pattern=document_pattern,
|
|
411
|
+
),
|
|
412
|
+
)
|
|
413
|
+
return on_document_updated_with_auth_context_wrapped
|
|
414
|
+
|
|
415
|
+
return on_document_updated_with_auth_context_inner_decorator
|
|
416
|
+
|
|
417
|
+
|
|
275
418
|
@_util.copy_func_kwargs(FirestoreOptions)
|
|
276
419
|
def on_document_created(**kwargs) -> _typing.Callable[[_C2], _C2]:
|
|
277
420
|
"""
|
|
@@ -320,6 +463,57 @@ def on_document_created(**kwargs) -> _typing.Callable[[_C2], _C2]:
|
|
|
320
463
|
return on_document_created_inner_decorator
|
|
321
464
|
|
|
322
465
|
|
|
466
|
+
@_util.copy_func_kwargs(FirestoreOptions)
|
|
467
|
+
def on_document_created_with_auth_context(**kwargs
|
|
468
|
+
) -> _typing.Callable[[_C2], _C2]:
|
|
469
|
+
"""
|
|
470
|
+
Event handler that triggers when a document is created in Firestore.
|
|
471
|
+
This trigger will also provide the authentication context of the principal who triggered
|
|
472
|
+
the event.
|
|
473
|
+
|
|
474
|
+
Example:
|
|
475
|
+
|
|
476
|
+
.. code-block:: python
|
|
477
|
+
|
|
478
|
+
@on_document_created_with_auth_context(document="*")
|
|
479
|
+
def example(event: AuthEvent[DocumentSnapshot]):
|
|
480
|
+
pass
|
|
481
|
+
|
|
482
|
+
:param \\*\\*kwargs: Firestore options.
|
|
483
|
+
:type \\*\\*kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
|
|
484
|
+
:rtype: :exc:`typing.Callable`
|
|
485
|
+
\\[ \\[ :exc:`firebase_functions.firestore_fn.AuthEvent` \\[
|
|
486
|
+
:exc:`object` \\] \\], `None` \\]
|
|
487
|
+
A function that takes a Firestore event and returns ``None``.
|
|
488
|
+
"""
|
|
489
|
+
options = FirestoreOptions(**kwargs)
|
|
490
|
+
|
|
491
|
+
def on_document_created_with_auth_context_inner_decorator(func: _C2):
|
|
492
|
+
document_pattern = _path_pattern.PathPattern(
|
|
493
|
+
_util.normalize_path(options.document))
|
|
494
|
+
|
|
495
|
+
@_functools.wraps(func)
|
|
496
|
+
def on_document_created_with_auth_context_wrapped(raw: _ce.CloudEvent):
|
|
497
|
+
return _firestore_endpoint_handler(
|
|
498
|
+
func,
|
|
499
|
+
_event_type_created_with_auth_context,
|
|
500
|
+
document_pattern,
|
|
501
|
+
raw,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
_util.set_func_endpoint_attr(
|
|
505
|
+
on_document_created_with_auth_context_wrapped,
|
|
506
|
+
options._endpoint(
|
|
507
|
+
event_type=_event_type_created_with_auth_context,
|
|
508
|
+
func_name=func.__name__,
|
|
509
|
+
document_pattern=document_pattern,
|
|
510
|
+
),
|
|
511
|
+
)
|
|
512
|
+
return on_document_created_with_auth_context_wrapped
|
|
513
|
+
|
|
514
|
+
return on_document_created_with_auth_context_inner_decorator
|
|
515
|
+
|
|
516
|
+
|
|
323
517
|
@_util.copy_func_kwargs(FirestoreOptions)
|
|
324
518
|
def on_document_deleted(**kwargs) -> _typing.Callable[[_C2], _C2]:
|
|
325
519
|
"""
|
|
@@ -366,3 +560,54 @@ def on_document_deleted(**kwargs) -> _typing.Callable[[_C2], _C2]:
|
|
|
366
560
|
return on_document_deleted_wrapped
|
|
367
561
|
|
|
368
562
|
return on_document_deleted_inner_decorator
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
@_util.copy_func_kwargs(FirestoreOptions)
|
|
566
|
+
def on_document_deleted_with_auth_context(**kwargs
|
|
567
|
+
) -> _typing.Callable[[_C2], _C2]:
|
|
568
|
+
"""
|
|
569
|
+
Event handler that triggers when a document is deleted in Firestore.
|
|
570
|
+
This trigger will also provide the authentication context of the principal who triggered
|
|
571
|
+
the event.
|
|
572
|
+
|
|
573
|
+
Example:
|
|
574
|
+
|
|
575
|
+
.. code-block:: python
|
|
576
|
+
|
|
577
|
+
@on_document_deleted_with_auth_context(document="*")
|
|
578
|
+
def example(event: AuthEvent[DocumentSnapshot]) -> None:
|
|
579
|
+
pass
|
|
580
|
+
|
|
581
|
+
:param \\*\\*kwargs: Firestore options.
|
|
582
|
+
:type \\*\\*kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
|
|
583
|
+
:rtype: :exc:`typing.Callable`
|
|
584
|
+
\\[ \\[ :exc:`firebase_functions.firestore_fn.AuthEvent` \\[
|
|
585
|
+
:exc:`object` \\] \\], `None` \\]
|
|
586
|
+
A function that takes a Firestore event and returns ``None``.
|
|
587
|
+
"""
|
|
588
|
+
options = FirestoreOptions(**kwargs)
|
|
589
|
+
|
|
590
|
+
def on_document_deleted_with_auth_context_inner_decorator(func: _C2):
|
|
591
|
+
document_pattern = _path_pattern.PathPattern(
|
|
592
|
+
_util.normalize_path(options.document))
|
|
593
|
+
|
|
594
|
+
@_functools.wraps(func)
|
|
595
|
+
def on_document_deleted_with_auth_context_wrapped(raw: _ce.CloudEvent):
|
|
596
|
+
return _firestore_endpoint_handler(
|
|
597
|
+
func,
|
|
598
|
+
_event_type_deleted_with_auth_context,
|
|
599
|
+
document_pattern,
|
|
600
|
+
raw,
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
_util.set_func_endpoint_attr(
|
|
604
|
+
on_document_deleted_with_auth_context_wrapped,
|
|
605
|
+
options._endpoint(
|
|
606
|
+
event_type=_event_type_deleted_with_auth_context,
|
|
607
|
+
func_name=func.__name__,
|
|
608
|
+
document_pattern=document_pattern,
|
|
609
|
+
),
|
|
610
|
+
)
|
|
611
|
+
return on_document_deleted_with_auth_context_wrapped
|
|
612
|
+
|
|
613
|
+
return on_document_deleted_with_auth_context_inner_decorator
|
|
@@ -135,6 +135,9 @@ class FunctionsErrorCode(str, _enum.Enum):
|
|
|
135
135
|
Unrecoverable data loss or corruption.
|
|
136
136
|
"""
|
|
137
137
|
|
|
138
|
+
def __str__(self) -> str:
|
|
139
|
+
return self.value
|
|
140
|
+
|
|
138
141
|
|
|
139
142
|
class _CanonicalErrorCodeName(str, _enum.Enum):
|
|
140
143
|
"""The canonical error code name for a given error code."""
|
|
@@ -157,6 +160,9 @@ class _CanonicalErrorCodeName(str, _enum.Enum):
|
|
|
157
160
|
UNAVAILABLE = "UNAVAILABLE"
|
|
158
161
|
DATA_LOSS = "DATA_LOSS"
|
|
159
162
|
|
|
163
|
+
def __str__(self) -> str:
|
|
164
|
+
return self.value
|
|
165
|
+
|
|
160
166
|
|
|
161
167
|
@_dataclasses.dataclass(frozen=True)
|
|
162
168
|
class _HttpErrorCode:
|
|
@@ -280,7 +286,7 @@ class AuthData:
|
|
|
280
286
|
The interface for Auth tokens verified in Callable functions
|
|
281
287
|
"""
|
|
282
288
|
|
|
283
|
-
uid: str
|
|
289
|
+
uid: str | None
|
|
284
290
|
"""
|
|
285
291
|
User ID of the ID token.
|
|
286
292
|
"""
|
|
@@ -346,8 +352,10 @@ _C1 = _typing.Callable[[Request], Response]
|
|
|
346
352
|
_C2 = _typing.Callable[[CallableRequest[_typing.Any]], _typing.Any]
|
|
347
353
|
|
|
348
354
|
|
|
349
|
-
def _on_call_handler(func: _C2,
|
|
350
|
-
|
|
355
|
+
def _on_call_handler(func: _C2,
|
|
356
|
+
request: Request,
|
|
357
|
+
enforce_app_check: bool,
|
|
358
|
+
verify_token: bool = True) -> Response:
|
|
351
359
|
try:
|
|
352
360
|
if not _util.valid_on_call_request(request):
|
|
353
361
|
_logging.error("Invalid request, unable to process.")
|
|
@@ -357,7 +365,8 @@ def _on_call_handler(func: _C2, request: Request,
|
|
|
357
365
|
data=_json.loads(request.data)["data"],
|
|
358
366
|
)
|
|
359
367
|
|
|
360
|
-
token_status = _util.on_call_check_tokens(request
|
|
368
|
+
token_status = _util.on_call_check_tokens(request,
|
|
369
|
+
verify_token=verify_token)
|
|
361
370
|
|
|
362
371
|
if token_status.auth == _util.OnCallTokenState.INVALID:
|
|
363
372
|
raise HttpsError(FunctionsErrorCode.UNAUTHENTICATED,
|
|
@@ -377,8 +386,10 @@ def _on_call_handler(func: _C2, request: Request,
|
|
|
377
386
|
if token_status.auth_token is not None:
|
|
378
387
|
context = _dataclasses.replace(
|
|
379
388
|
context,
|
|
380
|
-
auth=AuthData(
|
|
381
|
-
|
|
389
|
+
auth=AuthData(
|
|
390
|
+
token_status.auth_token["uid"]
|
|
391
|
+
if "uid" in token_status.auth_token else None,
|
|
392
|
+
token_status.auth_token),
|
|
382
393
|
)
|
|
383
394
|
|
|
384
395
|
instance_id = request.headers.get("Firebase-Instance-ID-Token")
|
|
@@ -392,14 +403,14 @@ def _on_call_handler(func: _C2, request: Request,
|
|
|
392
403
|
instance_id_token=request.headers.get(
|
|
393
404
|
"Firebase-Instance-ID-Token"),
|
|
394
405
|
)
|
|
395
|
-
result = func(context)
|
|
406
|
+
result = _core._with_init(func)(context)
|
|
396
407
|
return _jsonify(result=result)
|
|
397
408
|
# Disable broad exceptions lint since we want to handle all exceptions here
|
|
398
409
|
# and wrap as an HttpsError.
|
|
399
410
|
# pylint: disable=broad-except
|
|
400
411
|
except Exception as err:
|
|
401
412
|
if not isinstance(err, HttpsError):
|
|
402
|
-
_logging.error("Unhandled error", err)
|
|
413
|
+
_logging.error("Unhandled error: %s", err)
|
|
403
414
|
err = HttpsError(FunctionsErrorCode.INTERNAL, "INTERNAL")
|
|
404
415
|
status = err._http_error_code.status
|
|
405
416
|
return _make_response(_jsonify(error=err._as_dict()), status)
|
|
@@ -436,7 +447,7 @@ def on_request(**kwargs) -> _typing.Callable[[_C1], _C1]:
|
|
|
436
447
|
methods=options.cors.cors_methods,
|
|
437
448
|
origins=options.cors.cors_origins,
|
|
438
449
|
)(func)(request)
|
|
439
|
-
return func(request)
|
|
450
|
+
return _core._with_init(func)(request)
|
|
440
451
|
|
|
441
452
|
_util.set_func_endpoint_attr(
|
|
442
453
|
on_request_wrapped,
|
|
@@ -41,6 +41,9 @@ class VpcEgressSetting(str, _enum.Enum):
|
|
|
41
41
|
PRIVATE_RANGES_ONLY = "PRIVATE_RANGES_ONLY"
|
|
42
42
|
ALL_TRAFFIC = "ALL_TRAFFIC"
|
|
43
43
|
|
|
44
|
+
def __str__(self) -> str:
|
|
45
|
+
return self.value
|
|
46
|
+
|
|
44
47
|
|
|
45
48
|
class IngressSetting(str, _enum.Enum):
|
|
46
49
|
"""What kind of traffic can access the function."""
|
|
@@ -49,6 +52,9 @@ class IngressSetting(str, _enum.Enum):
|
|
|
49
52
|
ALLOW_INTERNAL_ONLY = "ALLOW_INTERNAL_ONLY"
|
|
50
53
|
ALLOW_INTERNAL_AND_GCLB = "ALLOW_INTERNAL_AND_GCLB"
|
|
51
54
|
|
|
55
|
+
def __str__(self) -> str:
|
|
56
|
+
return self.value
|
|
57
|
+
|
|
52
58
|
|
|
53
59
|
@_dataclasses.dataclass(frozen=True)
|
|
54
60
|
class CorsOptions:
|
|
@@ -88,6 +94,9 @@ class MemoryOption(int, _enum.Enum):
|
|
|
88
94
|
GB_16 = 16 << 10
|
|
89
95
|
GB_32 = 32 << 10
|
|
90
96
|
|
|
97
|
+
def __str__(self) -> str:
|
|
98
|
+
return f"{self.value}MB"
|
|
99
|
+
|
|
91
100
|
|
|
92
101
|
class SupportedRegion(str, _enum.Enum):
|
|
93
102
|
"""
|
|
@@ -120,6 +129,9 @@ class SupportedRegion(str, _enum.Enum):
|
|
|
120
129
|
US_WEST3 = "us-west3"
|
|
121
130
|
US_WEST4 = "us-west4"
|
|
122
131
|
|
|
132
|
+
def __str__(self) -> str:
|
|
133
|
+
return self.value
|
|
134
|
+
|
|
123
135
|
|
|
124
136
|
@_dataclasses.dataclass(frozen=True)
|
|
125
137
|
class RateLimits():
|
|
@@ -587,6 +599,9 @@ class AlertType(str, _enum.Enum):
|
|
|
587
599
|
Performance threshold alerts.
|
|
588
600
|
"""
|
|
589
601
|
|
|
602
|
+
def __str__(self) -> str:
|
|
603
|
+
return self.value
|
|
604
|
+
|
|
590
605
|
|
|
591
606
|
@_dataclasses.dataclass(frozen=True, kw_only=True)
|
|
592
607
|
class FirebaseAlertOptions(EventHandlerOptions):
|