Skip to content

Commit 4274b7f

Browse files
authored
fix: FCM push notification payload incompatible with Parse Android SDK (#238)
1 parent 92f86bd commit 4274b7f

File tree

3 files changed

+83
-46
lines changed

3 files changed

+83
-46
lines changed

spec/APNS.spec.js

+42
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,48 @@ describe('APNS', () => {
217217
done();
218218
});
219219

220+
it('can generate APNS notification with nested alert dictionary', (done) => {
221+
//Mock request data
222+
let data = {
223+
'alert': { body: 'alert', title: 'title' },
224+
'badge': 100,
225+
'sound': 'test',
226+
'content-available': 1,
227+
'mutable-content': 1,
228+
'targetContentIdentifier': 'window1',
229+
'interruptionLevel': 'passive',
230+
'category': 'INVITE_CATEGORY',
231+
'threadId': 'a-thread-id',
232+
'key': 'value',
233+
'keyAgain': 'valueAgain'
234+
};
235+
let expirationTime = 1454571491354;
236+
let collapseId = "collapseIdentifier";
237+
238+
let pushType = "alert";
239+
let priority = 5;
240+
let notification = APNS._generateNotification(data, { expirationTime: expirationTime, collapseId: collapseId, pushType: pushType, priority: priority });
241+
242+
expect(notification.aps.alert).toEqual({ body: 'alert', title: 'title' });
243+
expect(notification.aps.badge).toEqual(data.badge);
244+
expect(notification.aps.sound).toEqual(data.sound);
245+
expect(notification.aps['content-available']).toEqual(1);
246+
expect(notification.aps['mutable-content']).toEqual(1);
247+
expect(notification.aps['target-content-id']).toEqual('window1');
248+
expect(notification.aps['interruption-level']).toEqual('passive');
249+
expect(notification.aps.category).toEqual(data.category);
250+
expect(notification.aps['thread-id']).toEqual(data.threadId);
251+
expect(notification.payload).toEqual({
252+
'key': 'value',
253+
'keyAgain': 'valueAgain'
254+
});
255+
expect(notification.expiry).toEqual(Math.round(expirationTime / 1000));
256+
expect(notification.collapseId).toEqual(collapseId);
257+
expect(notification.pushType).toEqual(pushType);
258+
expect(notification.priority).toEqual(priority);
259+
done();
260+
});
261+
220262
it('sets push type to alert if not defined explicitly', (done) => {
221263
//Mock request data
222264
let data = {

spec/FCM.spec.js

+18-31
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ describe('FCM', () => {
6565
expect(payload.data.android).toEqual(requestData.rawPayload.android);
6666
expect(payload.data.apns).toEqual(requestData.rawPayload.apns);
6767
expect(payload.data.tokens).toEqual(['testToken']);
68-
expect(payload.time).toEqual(timeStampISOStr);
69-
expect(payload['push_id']).toEqual(pushId);
7068
});
7169

7270
it('can slice devices', () => {
@@ -87,7 +85,7 @@ describe('FCM', () => {
8785

8886
const requestData = {
8987
data: {
90-
alert: 'alert',
88+
alert: { body: 'alert', title: 'title' }
9189
},
9290
notification: {
9391
title: 'I am a title',
@@ -114,10 +112,10 @@ describe('FCM', () => {
114112
expect(fcmPayload.android.ttl).toEqual(undefined);
115113
expect(fcmPayload.android.notification).toEqual(requestData.notification);
116114

117-
expect(payload.time).toEqual(timeStampISOStr);
118-
expect(payload['push_id']).toEqual(pushId);
115+
expect(fcmPayload.android.data['time']).toEqual(timeStampISOStr);
116+
expect(fcmPayload.android.data['push_id']).toEqual(pushId);
119117

120-
const dataFromUser = fcmPayload.android.data;
118+
const dataFromUser = JSON.parse(fcmPayload.android.data.data);
121119
expect(dataFromUser).toEqual(requestData.data);
122120
});
123121

@@ -163,10 +161,10 @@ describe('FCM', () => {
163161
);
164162
expect(fcmPayload.android.notification).toEqual(requestData.notification);
165163

166-
expect(payload.time).toEqual(timeStampISOStr);
167-
expect(payload['push_id']).toEqual(pushId);
164+
expect(fcmPayload.android.data['time']).toEqual(timeStampISOStr);
165+
expect(fcmPayload.android.data['push_id']).toEqual(pushId);
168166

169-
const dataFromUser = fcmPayload.android.data;
167+
const dataFromUser = JSON.parse(fcmPayload.android.data.data);
170168
expect(dataFromUser).toEqual(requestData.data);
171169
});
172170

@@ -203,10 +201,10 @@ describe('FCM', () => {
203201
expect(fcmPayload.android.ttl).toEqual(0);
204202
expect(fcmPayload.android.notification).toEqual(requestData.notification);
205203

206-
expect(payload.time).toEqual(timeStampISOStr);
207-
expect(payload['push_id']).toEqual(pushId);
204+
expect(fcmPayload.android.data['time']).toEqual(timeStampISOStr);
205+
expect(fcmPayload.android.data['push_id']).toEqual(pushId);
208206

209-
const dataFromUser = fcmPayload.android.data;
207+
const dataFromUser = JSON.parse(fcmPayload.android.data.data);
210208
expect(dataFromUser).toEqual(requestData.data);
211209
});
212210

@@ -244,10 +242,10 @@ describe('FCM', () => {
244242
expect(fcmPayload.android.ttl).toEqual(4 * 7 * 24 * 60 * 60);
245243
expect(fcmPayload.android.notification).toEqual(requestData.notification);
246244

247-
expect(payload.time).toEqual(timeStampISOStr);
248-
expect(payload['push_id']).toEqual(pushId);
245+
expect(fcmPayload.android.data['time']).toEqual(timeStampISOStr);
246+
expect(fcmPayload.android.data['push_id']).toEqual(pushId);
249247

250-
const dataFromUser = fcmPayload.android.data;
248+
const dataFromUser = JSON.parse(fcmPayload.android.data.data);
251249
expect(dataFromUser).toEqual(requestData.data);
252250
});
253251
});
@@ -329,9 +327,6 @@ describe('FCM', () => {
329327
expect(fcmPayload.apns.headers['apns-collapse-id']).toEqual(collapseId);
330328
expect(fcmPayload.apns.headers['apns-push-type']).toEqual(pushType);
331329
expect(fcmPayload.apns.headers['apns-priority']).toEqual(priority);
332-
333-
expect(payload.time).toEqual(timeStampISOStr);
334-
expect(payload['push_id']).toEqual(pushId);
335330
});
336331

337332
it('sets push type to alert if not defined explicitly', () => {
@@ -348,9 +343,9 @@ describe('FCM', () => {
348343
keyAgain: 'valueAgain',
349344
};
350345

346+
// unused when generating apple payload, required by Parse Android SDK
351347
const pushId = 'pushId';
352348
const timeStamp = 1454538822113;
353-
const timeStampISOStr = new Date(timeStamp).toISOString();
354349

355350
const payload = FCM.generateFCMPayload(
356351
data,
@@ -362,8 +357,6 @@ describe('FCM', () => {
362357
const fcmPayload = payload.data;
363358

364359
expect(fcmPayload.apns.headers['apns-push-type']).toEqual('alert');
365-
expect(payload.time).toEqual(timeStampISOStr);
366-
expect(payload['push_id']).toEqual(pushId);
367360
});
368361

369362
it('can generate APNS notification from raw data', () => {
@@ -389,9 +382,9 @@ describe('FCM', () => {
389382
keyAgain: 'valueAgain',
390383
};
391384

385+
// unused when generating apple payload, required by Parse Android SDK
392386
const pushId = 'pushId';
393387
const timeStamp = 1454538822113;
394-
const timeStampISOStr = new Date(timeStamp).toISOString();
395388

396389
const payload = FCM.generateFCMPayload(
397390
data,
@@ -417,9 +410,6 @@ describe('FCM', () => {
417410
expect(fcmPayload.apns.payload.aps['thread-id']).toEqual('a-thread-id');
418411
expect(fcmPayload.apns.payload.key).toEqual('value');
419412
expect(fcmPayload.apns.payload.keyAgain).toEqual('valueAgain');
420-
421-
expect(payload.time).toEqual(timeStampISOStr);
422-
expect(payload['push_id']).toEqual(pushId);
423413
});
424414

425415
it('can generate an APNS notification with headers in data', () => {
@@ -433,16 +423,16 @@ describe('FCM', () => {
433423
let data = {
434424
expiration_time: expirationTime,
435425
data: {
436-
alert: 'alert',
426+
alert: { body: 'alert', title: 'title' },
437427
collapse_id: collapseId,
438428
push_type: pushType,
439429
priority: 6,
440430
},
441431
};
442432

433+
// unused when generating apple payload, required by Parse Android SDK
443434
const pushId = 'pushId';
444435
const timeStamp = 1454538822113;
445-
const timeStampISOStr = new Date(timeStamp).toISOString();
446436

447437
const payload = FCM.generateFCMPayload(
448438
data,
@@ -454,16 +444,13 @@ describe('FCM', () => {
454444

455445
const fcmPayload = payload.data;
456446

457-
expect(fcmPayload.apns.payload.aps.alert).toEqual({ body: 'alert' });
447+
expect(fcmPayload.apns.payload.aps.alert).toEqual({ body: 'alert', title: 'title' });
458448
expect(fcmPayload.apns.headers['apns-expiration']).toEqual(
459449
Math.round(expirationTime / 1000),
460450
);
461451
expect(fcmPayload.apns.headers['apns-collapse-id']).toEqual(collapseId);
462452
expect(fcmPayload.apns.headers['apns-push-type']).toEqual(pushType);
463453
expect(fcmPayload.apns.headers['apns-priority']).toEqual(6);
464-
465-
expect(payload.time).toEqual(timeStampISOStr);
466-
expect(payload['push_id']).toEqual(pushId);
467454
});
468455
});
469456

src/FCM.js

+23-15
Original file line numberDiff line numberDiff line change
@@ -178,20 +178,24 @@ function _APNSToFCMPayload(requestData) {
178178
apnsPayload['apns']['payload']['aps'] = coreData.aps;
179179
break;
180180
case 'alert':
181-
if (!apnsPayload['apns']['payload']['aps'].hasOwnProperty('alert')) {
181+
if (typeof coreData.alert == 'object') {
182+
// When we receive a dictionary, use as is to remain
183+
// compatible with how the APNS.js + node-apn work
184+
apnsPayload['apns']['payload']['aps']['alert'] = coreData.alert;
185+
} else {
186+
// When we receive a value, prepare `alert` dictionary
187+
// and set its `body` property
182188
apnsPayload['apns']['payload']['aps']['alert'] = {};
189+
apnsPayload['apns']['payload']['aps']['alert']['body'] = coreData.alert;
183190
}
184-
// In APNS.js we set a body with the same value as alert in requestData.
185-
// See L200 in APNS.spec.js
186-
apnsPayload['apns']['payload']['aps']['alert']['body'] = coreData.alert;
187191
break;
188192
case 'title':
189193
// Ensure the alert object exists before trying to assign the title
194+
// title always goes into the nested `alert` dictionary
190195
if (!apnsPayload['apns']['payload']['aps'].hasOwnProperty('alert')) {
191196
apnsPayload['apns']['payload']['aps']['alert'] = {};
192197
}
193-
apnsPayload['apns']['payload']['aps']['alert']['title'] =
194-
coreData.title;
198+
apnsPayload['apns']['payload']['aps']['alert']['title'] = coreData.title;
195199
break;
196200
case 'badge':
197201
apnsPayload['apns']['payload']['aps']['badge'] = coreData.badge;
@@ -237,7 +241,8 @@ function _APNSToFCMPayload(requestData) {
237241
return apnsPayload;
238242
}
239243

240-
function _GCMToFCMPayload(requestData, timeStamp) {
244+
function _GCMToFCMPayload(requestData, pushId, timeStamp) {
245+
241246
const androidPayload = {
242247
android: {
243248
priority: 'high',
@@ -255,7 +260,11 @@ function _GCMToFCMPayload(requestData, timeStamp) {
255260
delete requestData.data[key]
256261
}
257262
}
258-
androidPayload.android.data = requestData.data;
263+
androidPayload.android.data = {
264+
push_id: pushId,
265+
time: new Date(timeStamp).toISOString(),
266+
data: JSON.stringify(requestData.data),
267+
}
259268
}
260269

261270
if (requestData['expiration_time']) {
@@ -281,18 +290,19 @@ function _GCMToFCMPayload(requestData, timeStamp) {
281290
* If the key rawPayload is present in the requestData, a raw payload will be used. Otherwise, conversion is done.
282291
* @param {Object} requestData The request body
283292
* @param {String} pushType Either apple or android.
284-
* @param {Number} timeStamp Used during GCM payload conversion for ttl
293+
* @param {String} pushId Used during GCM payload conversion, required by Parse Android SDK.
294+
* @param {Number} timeStamp Used during GCM payload conversion for ttl, required by Parse Android SDK.
285295
* @returns {Object} A FCMv1-compatible payload.
286296
*/
287-
function payloadConverter(requestData, pushType, timeStamp) {
297+
function payloadConverter(requestData, pushType, pushId, timeStamp) {
288298
if (requestData.hasOwnProperty('rawPayload')) {
289299
return requestData.rawPayload;
290300
}
291301

292302
if (pushType === 'apple') {
293303
return _APNSToFCMPayload(requestData);
294304
} else if (pushType === 'android') {
295-
return _GCMToFCMPayload(requestData, timeStamp);
305+
return _GCMToFCMPayload(requestData, pushId, timeStamp);
296306
} else {
297307
throw new Parse.Error(
298308
Parse.Error.PUSH_MISCONFIGURED,
@@ -320,12 +330,10 @@ function generateFCMPayload(
320330
delete requestData['where'];
321331

322332
const payloadToUse = {
323-
data: {},
324-
push_id: pushId,
325-
time: new Date(timeStamp).toISOString(),
333+
data: {}
326334
};
327335

328-
const fcmPayload = payloadConverter(requestData, pushType, timeStamp);
336+
const fcmPayload = payloadConverter(requestData, pushType, pushId, timeStamp);
329337
payloadToUse.data = {
330338
...fcmPayload,
331339
tokens: deviceTokens,

0 commit comments

Comments
 (0)