1414
1515"""Firebase Cloud Messaging module."""
1616
17+ import concurrent .futures
1718import json
19+ import warnings
20+ import requests
1821
1922from googleapiclient import http
2023from googleapiclient import _auth
21- import requests
2224
2325import firebase_admin
2426from firebase_admin import _http_client
2527from firebase_admin import _messaging_encoder
2628from firebase_admin import _messaging_utils
2729from firebase_admin import _gapic_utils
2830from firebase_admin import _utils
31+ from firebase_admin import exceptions
2932
3033
3134_MESSAGING_ATTRIBUTE = '_messaging'
@@ -115,6 +118,57 @@ def send(message, dry_run=False, app=None):
115118 """
116119 return _get_messaging_service (app ).send (message , dry_run )
117120
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+
118172def send_all (messages , dry_run = False , app = None ):
119173 """Sends the given list of messages via Firebase Cloud Messaging as a single batch.
120174
@@ -132,7 +186,10 @@ def send_all(messages, dry_run=False, app=None):
132186 Raises:
133187 FirebaseError: If an error occurs while sending the message to the FCM service.
134188 ValueError: If the input arguments are invalid.
189+
190+ send_all() is deprecated. Use send_each() instead.
135191 """
192+ warnings .warn ('send_all() is deprecated. Use send_each() instead.' , DeprecationWarning )
136193 return _get_messaging_service (app ).send_all (messages , dry_run )
137194
138195def send_multicast (multicast_message , dry_run = False , app = None ):
@@ -152,7 +209,11 @@ def send_multicast(multicast_message, dry_run=False, app=None):
152209 Raises:
153210 FirebaseError: If an error occurs while sending the message to the FCM service.
154211 ValueError: If the input arguments are invalid.
212+
213+ send_multicast() is deprecated. Use send_each_for_multicast() instead.
155214 """
215+ warnings .warn ('send_multicast() is deprecated. Use send_each_for_multicast() instead.' ,
216+ DeprecationWarning )
156217 if not isinstance (multicast_message , MulticastMessage ):
157218 raise ValueError ('Message must be an instance of messaging.MulticastMessage class.' )
158219 messages = [Message (
@@ -356,6 +417,35 @@ def send(self, message, dry_run=False):
356417 else :
357418 return resp ['name' ]
358419
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+
359449 def send_all (self , messages , dry_run = False ):
360450 """Sends the given messages to FCM via the batch API."""
361451 if not isinstance (messages , list ):
0 commit comments