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 @@ class Event(_core.CloudEvent[_core.T]):
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 = _firestore .DocumentEventData .from_json (
98
- event_data )
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 = _firestore .DocumentEventData .deserialize (
102
- event_data )
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 = old_value_snapshot
175
+ firestore_event_data = _typing .cast (_firestore .DocumentEventData ,
176
+ old_value_snapshot )
150
177
if event_type == _event_type_created :
151
- firestore_event_data = value_snapshot
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 = Change (
154
- before = old_value_snapshot ,
155
- after = value_snapshot ,
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,15 @@ def _firestore_endpoint_handler(
173
204
subject = event_attributes ["subject" ],
174
205
params = params ,
175
206
)
176
- func (database_event )
207
+
208
+ if event_type .endswith (".withAuthContext" ):
209
+ database_event_with_auth_context = AuthEvent (** vars (database_event ),
210
+ auth_type = event_auth_type ,
211
+ auth_id = event_auth_id )
212
+ func (database_event_with_auth_context )
213
+ else :
214
+ # mypy cannot infer that the event type is correct, hence the cast
215
+ _typing .cast (_C1 | _C2 , func )(database_event )
177
216
178
217
179
218
@_util .copy_func_kwargs (FirestoreOptions )
@@ -224,6 +263,57 @@ def on_document_written_wrapped(raw: _ce.CloudEvent):
224
263
return on_document_written_inner_decorator
225
264
226
265
266
+ @_util .copy_func_kwargs (FirestoreOptions )
267
+ def on_document_written_with_auth_context (** kwargs
268
+ ) -> _typing .Callable [[_C1 ], _C1 ]:
269
+ """
270
+ Event handler that triggers when a document is created, updated, or deleted in Firestore.
271
+ This trigger will also provide the authentication context of the principal who triggered
272
+ the event.
273
+
274
+ Example:
275
+
276
+ .. code-block:: python
277
+
278
+ @on_document_written_with_auth_context(document="*")
279
+ def example(event: AuthEvent[Change[DocumentSnapshot]]) -> None:
280
+ pass
281
+
282
+ :param \\ *\\ *kwargs: Firestore options.
283
+ :type \\ *\\ *kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
284
+ :rtype: :exc:`typing.Callable`
285
+ \\ [ \\ [ :exc:`firebase_functions.firestore_fn.AuthEvent` \\ [
286
+ :exc:`firebase_functions.db.Change` \\ ] \\ ], `None` \\ ]
287
+ A function that takes a Firestore event and returns ``None``.
288
+ """
289
+ options = FirestoreOptions (** kwargs )
290
+
291
+ def on_document_written_with_auth_context_inner_decorator (func : _C1 ):
292
+ document_pattern = _path_pattern .PathPattern (
293
+ _util .normalize_path (options .document ))
294
+
295
+ @_functools .wraps (func )
296
+ def on_document_written_with_auth_context_wrapped (raw : _ce .CloudEvent ):
297
+ return _firestore_endpoint_handler (
298
+ func ,
299
+ _event_type_written_with_auth_context ,
300
+ document_pattern ,
301
+ raw ,
302
+ )
303
+
304
+ _util .set_func_endpoint_attr (
305
+ on_document_written_with_auth_context_wrapped ,
306
+ options ._endpoint (
307
+ event_type = _event_type_written ,
308
+ func_name = func .__name__ ,
309
+ document_pattern = document_pattern ,
310
+ ),
311
+ )
312
+ return on_document_written_with_auth_context_wrapped
313
+
314
+ return on_document_written_with_auth_context_inner_decorator
315
+
316
+
227
317
@_util .copy_func_kwargs (FirestoreOptions )
228
318
def on_document_updated (** kwargs ) -> _typing .Callable [[_C1 ], _C1 ]:
229
319
"""
@@ -272,6 +362,57 @@ def on_document_updated_wrapped(raw: _ce.CloudEvent):
272
362
return on_document_updated_inner_decorator
273
363
274
364
365
+ @_util .copy_func_kwargs (FirestoreOptions )
366
+ def on_document_updated_with_auth_context (** kwargs
367
+ ) -> _typing .Callable [[_C1 ], _C1 ]:
368
+ """
369
+ Event handler that triggers when a document is updated in Firestore.
370
+ This trigger will also provide the authentication context of the principal who triggered
371
+ the event.
372
+
373
+ Example:
374
+
375
+ .. code-block:: python
376
+
377
+ @on_document_updated_with_auth_context(document="*")
378
+ def example(event: AuthEvent[Change[DocumentSnapshot]]) -> None:
379
+ pass
380
+
381
+ :param \\ *\\ *kwargs: Firestore options.
382
+ :type \\ *\\ *kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
383
+ :rtype: :exc:`typing.Callable`
384
+ \\ [ \\ [ :exc:`firebase_functions.firestore_fn.AuthEvent` \\ [
385
+ :exc:`firebase_functions.db.Change` \\ ] \\ ], `None` \\ ]
386
+ A function that takes a Firestore event and returns ``None``.
387
+ """
388
+ options = FirestoreOptions (** kwargs )
389
+
390
+ def on_document_updated_with_auth_context_inner_decorator (func : _C1 ):
391
+ document_pattern = _path_pattern .PathPattern (
392
+ _util .normalize_path (options .document ))
393
+
394
+ @_functools .wraps (func )
395
+ def on_document_updated_with_auth_context_wrapped (raw : _ce .CloudEvent ):
396
+ return _firestore_endpoint_handler (
397
+ func ,
398
+ _event_type_updated_with_auth_context ,
399
+ document_pattern ,
400
+ raw ,
401
+ )
402
+
403
+ _util .set_func_endpoint_attr (
404
+ on_document_updated_with_auth_context_wrapped ,
405
+ options ._endpoint (
406
+ event_type = _event_type_updated_with_auth_context ,
407
+ func_name = func .__name__ ,
408
+ document_pattern = document_pattern ,
409
+ ),
410
+ )
411
+ return on_document_updated_with_auth_context_wrapped
412
+
413
+ return on_document_updated_with_auth_context_inner_decorator
414
+
415
+
275
416
@_util .copy_func_kwargs (FirestoreOptions )
276
417
def on_document_created (** kwargs ) -> _typing .Callable [[_C2 ], _C2 ]:
277
418
"""
@@ -320,6 +461,57 @@ def on_document_created_wrapped(raw: _ce.CloudEvent):
320
461
return on_document_created_inner_decorator
321
462
322
463
464
+ @_util .copy_func_kwargs (FirestoreOptions )
465
+ def on_document_created_with_auth_context (** kwargs
466
+ ) -> _typing .Callable [[_C2 ], _C2 ]:
467
+ """
468
+ Event handler that triggers when a document is created in Firestore.
469
+ This trigger will also provide the authentication context of the principal who triggered
470
+ the event.
471
+
472
+ Example:
473
+
474
+ .. code-block:: python
475
+
476
+ @on_document_created_with_auth_context(document="*")
477
+ def example(event: AuthEvent[DocumentSnapshot]):
478
+ pass
479
+
480
+ :param \\ *\\ *kwargs: Firestore options.
481
+ :type \\ *\\ *kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
482
+ :rtype: :exc:`typing.Callable`
483
+ \\ [ \\ [ :exc:`firebase_functions.firestore_fn.AuthEvent` \\ [
484
+ :exc:`object` \\ ] \\ ], `None` \\ ]
485
+ A function that takes a Firestore event and returns ``None``.
486
+ """
487
+ options = FirestoreOptions (** kwargs )
488
+
489
+ def on_document_created_with_auth_context_inner_decorator (func : _C2 ):
490
+ document_pattern = _path_pattern .PathPattern (
491
+ _util .normalize_path (options .document ))
492
+
493
+ @_functools .wraps (func )
494
+ def on_document_created_with_auth_context_wrapped (raw : _ce .CloudEvent ):
495
+ return _firestore_endpoint_handler (
496
+ func ,
497
+ _event_type_created_with_auth_context ,
498
+ document_pattern ,
499
+ raw ,
500
+ )
501
+
502
+ _util .set_func_endpoint_attr (
503
+ on_document_created_with_auth_context_wrapped ,
504
+ options ._endpoint (
505
+ event_type = _event_type_created_with_auth_context ,
506
+ func_name = func .__name__ ,
507
+ document_pattern = document_pattern ,
508
+ ),
509
+ )
510
+ return on_document_created_with_auth_context_wrapped
511
+
512
+ return on_document_created_with_auth_context_inner_decorator
513
+
514
+
323
515
@_util .copy_func_kwargs (FirestoreOptions )
324
516
def on_document_deleted (** kwargs ) -> _typing .Callable [[_C2 ], _C2 ]:
325
517
"""
@@ -366,3 +558,54 @@ def on_document_deleted_wrapped(raw: _ce.CloudEvent):
366
558
return on_document_deleted_wrapped
367
559
368
560
return on_document_deleted_inner_decorator
561
+
562
+
563
+ @_util .copy_func_kwargs (FirestoreOptions )
564
+ def on_document_deleted_with_auth_context (** kwargs
565
+ ) -> _typing .Callable [[_C2 ], _C2 ]:
566
+ """
567
+ Event handler that triggers when a document is deleted in Firestore.
568
+ This trigger will also provide the authentication context of the principal who triggered
569
+ the event.
570
+
571
+ Example:
572
+
573
+ .. code-block:: python
574
+
575
+ @on_document_deleted_with_auth_context(document="*")
576
+ def example(event: AuthEvent[DocumentSnapshot]) -> None:
577
+ pass
578
+
579
+ :param \\ *\\ *kwargs: Firestore options.
580
+ :type \\ *\\ *kwargs: as :exc:`firebase_functions.options.FirestoreOptions`
581
+ :rtype: :exc:`typing.Callable`
582
+ \\ [ \\ [ :exc:`firebase_functions.firestore_fn.AuthEvent` \\ [
583
+ :exc:`object` \\ ] \\ ], `None` \\ ]
584
+ A function that takes a Firestore event and returns ``None``.
585
+ """
586
+ options = FirestoreOptions (** kwargs )
587
+
588
+ def on_document_deleted_with_auth_context_inner_decorator (func : _C2 ):
589
+ document_pattern = _path_pattern .PathPattern (
590
+ _util .normalize_path (options .document ))
591
+
592
+ @_functools .wraps (func )
593
+ def on_document_deleted_with_auth_context_wrapped (raw : _ce .CloudEvent ):
594
+ return _firestore_endpoint_handler (
595
+ func ,
596
+ _event_type_deleted_with_auth_context ,
597
+ document_pattern ,
598
+ raw ,
599
+ )
600
+
601
+ _util .set_func_endpoint_attr (
602
+ on_document_deleted_with_auth_context_wrapped ,
603
+ options ._endpoint (
604
+ event_type = _event_type_deleted_with_auth_context ,
605
+ func_name = func .__name__ ,
606
+ document_pattern = document_pattern ,
607
+ ),
608
+ )
609
+ return on_document_deleted_with_auth_context_wrapped
610
+
611
+ return on_document_deleted_with_auth_context_inner_decorator
0 commit comments