14
14
15
15
"""Firebase Cloud Messaging module."""
16
16
17
+ import concurrent .futures
17
18
import json
19
+ import warnings
20
+ import requests
18
21
19
22
from googleapiclient import http
20
23
from googleapiclient import _auth
21
- import requests
22
24
23
25
import firebase_admin
24
26
from firebase_admin import _http_client
25
27
from firebase_admin import _messaging_encoder
26
28
from firebase_admin import _messaging_utils
27
29
from firebase_admin import _gapic_utils
28
30
from firebase_admin import _utils
31
+ from firebase_admin import exceptions
29
32
30
33
31
34
_MESSAGING_ATTRIBUTE = '_messaging'
@@ -115,6 +118,57 @@ def send(message, dry_run=False, app=None):
115
118
"""
116
119
return _get_messaging_service (app ).send (message , dry_run )
117
120
121
+ def send_each (messages , dry_run = False , app = None ):
122
+ """Sends each message in the given list via Firebase Cloud Messaging.
123
+
124
+ If the ``dry_run`` mode is enabled, the message will not be actually delivered to the
125
+ recipients. Instead FCM performs all the usual validations, and emulates the send operation.
126
+
127
+ Args:
128
+ messages: A list of ``messaging.Message`` instances.
129
+ dry_run: A boolean indicating whether to run the operation in dry run mode (optional).
130
+ app: An App instance (optional).
131
+
132
+ Returns:
133
+ BatchResponse: A ``messaging.BatchResponse`` instance.
134
+
135
+ Raises:
136
+ FirebaseError: If an error occurs while sending the message to the FCM service.
137
+ ValueError: If the input arguments are invalid.
138
+ """
139
+ return _get_messaging_service (app ).send_each (messages , dry_run )
140
+
141
+ def send_each_for_multicast (multicast_message , dry_run = False , app = None ):
142
+ """Sends the given mutlicast message to each token via Firebase Cloud Messaging (FCM).
143
+
144
+ If the ``dry_run`` mode is enabled, the message will not be actually delivered to the
145
+ recipients. Instead FCM performs all the usual validations, and emulates the send operation.
146
+
147
+ Args:
148
+ multicast_message: An instance of ``messaging.MulticastMessage``.
149
+ dry_run: A boolean indicating whether to run the operation in dry run mode (optional).
150
+ app: An App instance (optional).
151
+
152
+ Returns:
153
+ BatchResponse: A ``messaging.BatchResponse`` instance.
154
+
155
+ Raises:
156
+ FirebaseError: If an error occurs while sending the message to the FCM service.
157
+ ValueError: If the input arguments are invalid.
158
+ """
159
+ if not isinstance (multicast_message , MulticastMessage ):
160
+ raise ValueError ('Message must be an instance of messaging.MulticastMessage class.' )
161
+ messages = [Message (
162
+ data = multicast_message .data ,
163
+ notification = multicast_message .notification ,
164
+ android = multicast_message .android ,
165
+ webpush = multicast_message .webpush ,
166
+ apns = multicast_message .apns ,
167
+ fcm_options = multicast_message .fcm_options ,
168
+ token = token
169
+ ) for token in multicast_message .tokens ]
170
+ return _get_messaging_service (app ).send_each (messages , dry_run )
171
+
118
172
def send_all (messages , dry_run = False , app = None ):
119
173
"""Sends the given list of messages via Firebase Cloud Messaging as a single batch.
120
174
@@ -132,7 +186,10 @@ def send_all(messages, dry_run=False, app=None):
132
186
Raises:
133
187
FirebaseError: If an error occurs while sending the message to the FCM service.
134
188
ValueError: If the input arguments are invalid.
189
+
190
+ send_all() is deprecated. Use send_each() instead.
135
191
"""
192
+ warnings .warn ('send_all() is deprecated. Use send_each() instead.' , DeprecationWarning )
136
193
return _get_messaging_service (app ).send_all (messages , dry_run )
137
194
138
195
def send_multicast (multicast_message , dry_run = False , app = None ):
@@ -152,7 +209,11 @@ def send_multicast(multicast_message, dry_run=False, app=None):
152
209
Raises:
153
210
FirebaseError: If an error occurs while sending the message to the FCM service.
154
211
ValueError: If the input arguments are invalid.
212
+
213
+ send_multicast() is deprecated. Use send_each_for_multicast() instead.
155
214
"""
215
+ warnings .warn ('send_multicast() is deprecated. Use send_each_for_multicast() instead.' ,
216
+ DeprecationWarning )
156
217
if not isinstance (multicast_message , MulticastMessage ):
157
218
raise ValueError ('Message must be an instance of messaging.MulticastMessage class.' )
158
219
messages = [Message (
@@ -356,6 +417,35 @@ def send(self, message, dry_run=False):
356
417
else :
357
418
return resp ['name' ]
358
419
420
+ def send_each (self , messages , dry_run = False ):
421
+ """Sends the given messages to FCM via the FCM v1 API."""
422
+ if not isinstance (messages , list ):
423
+ raise ValueError ('messages must be a list of messaging.Message instances.' )
424
+ if len (messages ) > 500 :
425
+ raise ValueError ('messages must not contain more than 500 elements.' )
426
+
427
+ def send_data (data ):
428
+ try :
429
+ resp = self ._client .body (
430
+ 'post' ,
431
+ url = self ._fcm_url ,
432
+ headers = self ._fcm_headers ,
433
+ json = data )
434
+ except requests .exceptions .RequestException as exception :
435
+ return SendResponse (resp = None , exception = self ._handle_fcm_error (exception ))
436
+ else :
437
+ return SendResponse (resp , exception = None )
438
+
439
+ message_data = [self ._message_data (message , dry_run ) for message in messages ]
440
+ try :
441
+ with concurrent .futures .ThreadPoolExecutor (max_workers = len (message_data )) as executor :
442
+ responses = [resp for resp in executor .map (send_data , message_data )]
443
+ return BatchResponse (responses )
444
+ except Exception as error :
445
+ raise exceptions .UnknownError (
446
+ message = 'Unknown error while making remote service calls: {0}' .format (error ),
447
+ cause = error )
448
+
359
449
def send_all (self , messages , dry_run = False ):
360
450
"""Sends the given messages to FCM via the batch API."""
361
451
if not isinstance (messages , list ):
0 commit comments