@@ -22,8 +22,12 @@ import { validateMessage, BLACKLISTED_DATA_PAYLOAD_KEYS, BLACKLISTED_OPTIONS_KEY
22
22
import { messaging } from './index' ;
23
23
import { FirebaseMessagingRequestHandler } from './messaging-api-request-internal' ;
24
24
import { ErrorInfo , MessagingClientErrorCode , FirebaseMessagingError } from '../utils/error' ;
25
+ import { createFirebaseErrorFromGapicError } from './messaging-errors-internal' ;
26
+ import { ServiceAccountCredential } from '../credential/credential-internal' ;
25
27
import * as utils from '../utils' ;
26
28
import * as validator from '../utils/validator' ;
29
+ import * as protos from '../generated/messaging/protos/protos' ;
30
+ import { FcmServiceClient } from '../generated/messaging/src/v1/fcm_service_client'
27
31
28
32
import MessagingInterface = messaging . Messaging ;
29
33
import Message = messaging . Message ;
@@ -41,6 +45,10 @@ import MessagingConditionResponse = messaging.MessagingConditionResponse;
41
45
import DataMessagePayload = messaging . DataMessagePayload ;
42
46
import NotificationMessagePayload = messaging . NotificationMessagePayload ;
43
47
48
+ // GAPIC Generated client types
49
+ import IMessage = protos . google . firebase . fcm . v1 . IMessage ;
50
+ import ISendRequest = protos . google . firebase . fcm . v1 . ISendMessageRequest ;
51
+
44
52
/* eslint-disable @typescript-eslint/camelcase */
45
53
46
54
// FCM endpoints
@@ -193,6 +201,7 @@ export class Messaging implements MessagingInterface {
193
201
private urlPath : string ;
194
202
private readonly appInternal : FirebaseApp ;
195
203
private readonly messagingRequestHandler : FirebaseMessagingRequestHandler ;
204
+ private fcmServiceClient : FcmServiceClient ;
196
205
197
206
/**
198
207
* Gets the {@link messaging.Messaging `Messaging`} service for the
@@ -228,6 +237,77 @@ export class Messaging implements MessagingInterface {
228
237
return this . appInternal ;
229
238
}
230
239
240
+
241
+ /**
242
+ * Converts from the public {@link messaging.Message `Message`} type to the
243
+ * internal/generated {@link protos.google.firebase.fcm.v1.Message `clientMessage`}
244
+ * type.
245
+ *
246
+ * @param message The {@link messaging.Message `Message`} to be converted
247
+ * @returns A {@link protos.google.firebase.fcm.v1.Message `clientMessage`},
248
+ * where like fields are the same as the inptuted message.
249
+ */
250
+ private convertToIMessage ( message : Message ) : IMessage {
251
+ //TODO: Add conversion for android, webpush, apns
252
+
253
+ const convertedMessage : IMessage = {
254
+ data : message . data ,
255
+ notification : message . notification ,
256
+ fcmOptions : message . fcmOptions
257
+ }
258
+
259
+ if ( 'token' in message ) {
260
+ convertedMessage . token = message . token ;
261
+ } else if ( 'topic' in message ) {
262
+ convertedMessage . topic = message . topic ;
263
+ } else if ( 'condition' in message ) {
264
+ convertedMessage . condition = message . condition ;
265
+ }
266
+
267
+ return convertedMessage ;
268
+ }
269
+
270
+ /**
271
+ * Method that returns a fully instantiated service client.
272
+ *
273
+ * @returns an instantiated service client with either the application's
274
+ * service account credentials or the default credentials
275
+ */
276
+ private getFcmServiceClient ( ) : Promise < FcmServiceClient > {
277
+ if ( this . fcmServiceClient ) {
278
+ return Promise . resolve ( this . fcmServiceClient ) ;
279
+ }
280
+
281
+ const credential = this . app . options . credential ;
282
+ const options : { credentials ?: object ; fallback : 'rest' ; projectId ?: string } = {
283
+ fallback : 'rest'
284
+ } ;
285
+
286
+ if ( credential instanceof ServiceAccountCredential ) {
287
+ options . credentials = {
288
+ private_key : credential . privateKey ,
289
+ client_email : credential . clientEmail
290
+ } ;
291
+ }
292
+
293
+ return utils . findProjectId ( this . appInternal ) . then ( projectId => {
294
+ if ( ! validator . isNonEmptyString ( projectId ) ) {
295
+ // Assert for an explicit project ID (either via AppOptions or the cert itself).
296
+ throw new FirebaseMessagingError (
297
+ MessagingClientErrorCode . INVALID_ARGUMENT ,
298
+ 'Failed to determine project ID for Messaging. Initialize the '
299
+ + 'SDK with service account credentials or set project ID as an app option. '
300
+ + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.' ,
301
+ ) ;
302
+ }
303
+
304
+ options . projectId = projectId ;
305
+ } ) . then ( ( ) => {
306
+ this . fcmServiceClient = new FcmServiceClient ( options ) ;
307
+ return this . fcmServiceClient ;
308
+ } )
309
+ }
310
+
231
311
/**
232
312
* Sends the given message via FCM.
233
313
*
@@ -245,16 +325,27 @@ export class Messaging implements MessagingInterface {
245
325
throw new FirebaseMessagingError (
246
326
MessagingClientErrorCode . INVALID_ARGUMENT , 'dryRun must be a boolean' ) ;
247
327
}
248
- return this . getUrlPath ( )
249
- . then ( ( urlPath ) => {
250
- const request : { message : Message ; validate_only ?: boolean } = { message : copy } ;
251
- if ( dryRun ) {
252
- request . validate_only = true ;
253
- }
254
- return this . messagingRequestHandler . invokeRequestHandler ( FCM_SEND_HOST , urlPath , request ) ;
255
- } )
256
- . then ( ( response ) => {
257
- return ( response as any ) . name ;
328
+
329
+ const IMessage = this . convertToIMessage ( copy ) ;
330
+
331
+ return this . getFcmServiceClient ( )
332
+ . then ( client => {
333
+ return client . getProjectId ( )
334
+ . then ( ( projectId ) => {
335
+ const parent = `projects/${ projectId } ` ;
336
+
337
+ const request : ISendRequest = {
338
+ parent : parent ,
339
+ message : IMessage ,
340
+ validateOnly : dryRun
341
+ } ;
342
+ return client . sendMessage ( request ) ;
343
+ } )
344
+ . then ( ( [ response ] ) => {
345
+ return response . name ! ;
346
+ } ) . catch ( err => {
347
+ throw createFirebaseErrorFromGapicError ( err ) ;
348
+ } ) ;
258
349
} ) ;
259
350
}
260
351
0 commit comments