@@ -39,6 +39,7 @@ import {
3939 MessagingConditionResponse ,
4040 DataMessagePayload ,
4141 NotificationMessagePayload ,
42+ SendResponse ,
4243} from './messaging-api' ;
4344
4445// FCM endpoints
@@ -250,6 +251,124 @@ export class Messaging {
250251 } ) ;
251252 }
252253
254+ /**
255+ * Sends each message in the given array via Firebase Cloud Messaging.
256+ *
257+ * Unlike {@link Messaging.sendAll}, this method makes a single RPC call for each message
258+ * in the given array.
259+ *
260+ * The responses list obtained from the return value corresponds to the order of `messages`.
261+ * An error from this method or a `BatchResponse` with all failures indicates a total failure,
262+ * meaning that none of the messages in the list could be sent. Partial failures or no
263+ * failures are only indicated by a `BatchResponse` return value.
264+ *
265+ * @param messages - A non-empty array
266+ * containing up to 500 messages.
267+ * @param dryRun - Whether to send the messages in the dry-run
268+ * (validation only) mode.
269+ * @returns A Promise fulfilled with an object representing the result of the
270+ * send operation.
271+ */
272+ public sendEach ( messages : Message [ ] , dryRun ?: boolean ) : Promise < BatchResponse > {
273+ if ( validator . isArray ( messages ) && messages . constructor !== Array ) {
274+ // In more recent JS specs, an array-like object might have a constructor that is not of
275+ // Array type. Our deepCopy() method doesn't handle them properly. Convert such objects to
276+ // a regular array here before calling deepCopy(). See issue #566 for details.
277+ messages = Array . from ( messages ) ;
278+ }
279+
280+ const copy : Message [ ] = deepCopy ( messages ) ;
281+ if ( ! validator . isNonEmptyArray ( copy ) ) {
282+ throw new FirebaseMessagingError (
283+ MessagingClientErrorCode . INVALID_ARGUMENT , 'messages must be a non-empty array' ) ;
284+ }
285+ if ( copy . length > FCM_MAX_BATCH_SIZE ) {
286+ throw new FirebaseMessagingError (
287+ MessagingClientErrorCode . INVALID_ARGUMENT ,
288+ `messages list must not contain more than ${ FCM_MAX_BATCH_SIZE } items` ) ;
289+ }
290+ if ( typeof dryRun !== 'undefined' && ! validator . isBoolean ( dryRun ) ) {
291+ throw new FirebaseMessagingError (
292+ MessagingClientErrorCode . INVALID_ARGUMENT , 'dryRun must be a boolean' ) ;
293+ }
294+
295+ return this . getUrlPath ( )
296+ . then ( ( urlPath ) => {
297+ const requests : Promise < SendResponse > [ ] = copy . map ( ( message ) => {
298+ validateMessage ( message ) ;
299+ const request : { message : Message ; validate_only ?: boolean } = { message } ;
300+ if ( dryRun ) {
301+ request . validate_only = true ;
302+ }
303+ return this . messagingRequestHandler . invokeRequestHandlerForSendResponse ( FCM_SEND_HOST , urlPath , request ) ;
304+ } ) ;
305+ return Promise . allSettled ( requests ) ;
306+ } ) . then ( ( results ) => {
307+ const responses : SendResponse [ ] = [ ] ;
308+ results . forEach ( result => {
309+ if ( result . status === 'fulfilled' ) {
310+ responses . push ( result . value ) ;
311+ } else { // rejected
312+ responses . push ( { success : false , error : result . reason } )
313+ }
314+ } )
315+ const successCount : number = responses . filter ( ( resp ) => resp . success ) . length ;
316+ return {
317+ responses,
318+ successCount,
319+ failureCount : responses . length - successCount ,
320+ } ;
321+ } ) ;
322+ }
323+
324+ /**
325+ * Sends the given multicast message to all the FCM registration tokens
326+ * specified in it.
327+ *
328+ * This method uses the {@link Messaging.sendEach} API under the hood to send the given
329+ * message to all the target recipients. The responses list obtained from the
330+ * return value corresponds to the order of tokens in the `MulticastMessage`.
331+ * An error from this method or a `BatchResponse` with all failures indicates a total
332+ * failure, meaning that the messages in the list could be sent. Partial failures or
333+ * failures are only indicated by a `BatchResponse` return value.
334+ *
335+ * @param message - A multicast message
336+ * containing up to 500 tokens.
337+ * @param dryRun - Whether to send the message in the dry-run
338+ * (validation only) mode.
339+ * @returns A Promise fulfilled with an object representing the result of the
340+ * send operation.
341+ */
342+ public sendEachForMulticast ( message : MulticastMessage , dryRun ?: boolean ) : Promise < BatchResponse > {
343+ const copy : MulticastMessage = deepCopy ( message ) ;
344+ if ( ! validator . isNonNullObject ( copy ) ) {
345+ throw new FirebaseMessagingError (
346+ MessagingClientErrorCode . INVALID_ARGUMENT , 'MulticastMessage must be a non-null object' ) ;
347+ }
348+ if ( ! validator . isNonEmptyArray ( copy . tokens ) ) {
349+ throw new FirebaseMessagingError (
350+ MessagingClientErrorCode . INVALID_ARGUMENT , 'tokens must be a non-empty array' ) ;
351+ }
352+ if ( copy . tokens . length > FCM_MAX_BATCH_SIZE ) {
353+ throw new FirebaseMessagingError (
354+ MessagingClientErrorCode . INVALID_ARGUMENT ,
355+ `tokens list must not contain more than ${ FCM_MAX_BATCH_SIZE } items` ) ;
356+ }
357+
358+ const messages : Message [ ] = copy . tokens . map ( ( token ) => {
359+ return {
360+ token,
361+ android : copy . android ,
362+ apns : copy . apns ,
363+ data : copy . data ,
364+ notification : copy . notification ,
365+ webpush : copy . webpush ,
366+ fcmOptions : copy . fcmOptions ,
367+ } ;
368+ } ) ;
369+ return this . sendEach ( messages , dryRun ) ;
370+ }
371+
253372 /**
254373 * Sends all the messages in the given array via Firebase Cloud Messaging.
255374 * Employs batching to send the entire list as a single RPC call. Compared
@@ -258,8 +377,8 @@ export class Messaging {
258377 *
259378 * The responses list obtained from the return value
260379 * corresponds to the order of tokens in the `MulticastMessage`. An error
261- * from this method indicates a total failure -- i.e. none of the messages in
262- * the list could be sent. Partial failures are indicated by a `BatchResponse`
380+ * from this method indicates a total failure, meaning that none of the messages
381+ * in the list could be sent. Partial failures are indicated by a `BatchResponse`
263382 * return value.
264383 *
265384 * @param messages - A non-empty array
@@ -268,6 +387,8 @@ export class Messaging {
268387 * (validation only) mode.
269388 * @returns A Promise fulfilled with an object representing the result of the
270389 * send operation.
390+ *
391+ * @deprecated Use {@link Messaging.sendEach} instead.
271392 */
272393 public sendAll ( messages : Message [ ] , dryRun ?: boolean ) : Promise < BatchResponse > {
273394 if ( validator . isArray ( messages ) && messages . constructor !== Array ) {
@@ -316,16 +437,18 @@ export class Messaging {
316437 * This method uses the `sendAll()` API under the hood to send the given
317438 * message to all the target recipients. The responses list obtained from the
318439 * return value corresponds to the order of tokens in the `MulticastMessage`.
319- * An error from this method indicates a total failure -- i.e. the message was
320- * not sent to any of the tokens in the list. Partial failures are indicated by
321- * a `BatchResponse` return value.
440+ * An error from this method indicates a total failure, meaning that the message
441+ * was not sent to any of the tokens in the list. Partial failures are indicated
442+ * by a `BatchResponse` return value.
322443 *
323444 * @param message - A multicast message
324445 * containing up to 500 tokens.
325446 * @param dryRun - Whether to send the message in the dry-run
326447 * (validation only) mode.
327448 * @returns A Promise fulfilled with an object representing the result of the
328449 * send operation.
450+ *
451+ * @deprecated Use {@link Messaging.sendEachForMulticast} instead.
329452 */
330453 public sendMulticast ( message : MulticastMessage , dryRun ?: boolean ) : Promise < BatchResponse > {
331454 const copy : MulticastMessage = deepCopy ( message ) ;
0 commit comments