Skip to content

Commit c2c4f51

Browse files
committed
Push notifications to APNS per batches
1 parent d789ef6 commit c2c4f51

File tree

2 files changed

+155
-18
lines changed

2 files changed

+155
-18
lines changed

spec/APNS.spec.js

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,18 @@ describe('APNS', () => {
290290
{
291291
deviceToken: '112233',
292292
appIdentifier: 'bundleId'
293+
},
294+
{
295+
deviceToken: '112234',
296+
appIdentifier: 'bundleId'
297+
},
298+
{
299+
deviceToken: '112235',
300+
appIdentifier: 'bundleId'
301+
},
302+
{
303+
deviceToken: '112236',
304+
appIdentifier: 'bundleId'
293305
}
294306
];
295307

@@ -299,9 +311,94 @@ describe('APNS', () => {
299311
var notification = args[0];
300312
expect(notification.alert).toEqual(data.data.alert);
301313
expect(notification.expiry).toEqual(data['expiration_time']/1000);
302-
var apnDevice = args[1]
303-
expect(apnDevice.connIndex).toEqual(0);
304-
expect(apnDevice.appIdentifier).toEqual('bundleId');
314+
var apnDevices = args[1];
315+
apnDevices.forEach((apnDevice) => {
316+
expect(apnDevice.connIndex).toEqual(0);
317+
expect(apnDevice.appIdentifier).toEqual('bundleId');
318+
})
319+
done();
320+
});
321+
322+
it('can send APNS notification to multiple bundles', (done) => {
323+
var args = [{
324+
cert: 'prodCert.pem',
325+
key: 'prodKey.pem',
326+
production: true,
327+
bundleId: 'bundleId'
328+
},{
329+
cert: 'devCert.pem',
330+
key: 'devKey.pem',
331+
production: false,
332+
bundleId: 'bundleId.dev'
333+
}];
334+
335+
var apns = new APNS(args);
336+
var conn = {
337+
pushNotification: jasmine.createSpy('send'),
338+
bundleId: 'bundleId'
339+
};
340+
var conndev = {
341+
pushNotification: jasmine.createSpy('send'),
342+
bundleId: 'bundleId.dev'
343+
};
344+
apns.conns = [ conn, conndev ];
345+
// Mock data
346+
var expirationTime = 1454571491354
347+
var data = {
348+
'expiration_time': expirationTime,
349+
'data': {
350+
'alert': 'alert'
351+
}
352+
}
353+
// Mock devices
354+
var devices = [
355+
{
356+
deviceToken: '112233',
357+
appIdentifier: 'bundleId'
358+
},
359+
{
360+
deviceToken: '112234',
361+
appIdentifier: 'bundleId'
362+
},
363+
{
364+
deviceToken: '112235',
365+
appIdentifier: 'bundleId'
366+
},
367+
{
368+
deviceToken: '112235',
369+
appIdentifier: 'bundleId.dev'
370+
},
371+
{
372+
deviceToken: '112236',
373+
appIdentifier: 'bundleId.dev'
374+
}
375+
];
376+
377+
var promise = apns.send(data, devices);
378+
379+
expect(conn.pushNotification).toHaveBeenCalled();
380+
var args = conn.pushNotification.calls.first().args;
381+
var notification = args[0];
382+
expect(notification.alert).toEqual(data.data.alert);
383+
expect(notification.expiry).toEqual(data['expiration_time']/1000);
384+
var apnDevices = args[1];
385+
expect(apnDevices.length).toBe(3);
386+
apnDevices.forEach((apnDevice) => {
387+
expect(apnDevice.connIndex).toEqual(0);
388+
expect(apnDevice.appIdentifier).toEqual('bundleId');
389+
})
390+
391+
expect(conndev.pushNotification).toHaveBeenCalled();
392+
args = conndev.pushNotification.calls.first().args;
393+
notification = args[0];
394+
expect(notification.alert).toEqual(data.data.alert);
395+
expect(notification.expiry).toEqual(data['expiration_time']/1000);
396+
apnDevices = args[1];
397+
expect(apnDevices.length).toBe(2);
398+
apnDevices.forEach((apnDevice) => {
399+
expect(apnDevice.connIndex).toEqual(1);
400+
expect(apnDevice.appIdentifier).toEqual('bundleId.dev');
401+
});
305402
done();
306403
});
307404
});

src/APNS.js

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,34 +104,74 @@ APNS.prototype.send = function(data, devices) {
104104
let coreData = data.data;
105105
let expirationTime = data['expiration_time'];
106106
let notification = generateNotification(coreData, expirationTime);
107+
let allPromises = [];
107108

108-
let promises = devices.map((device) => {
109+
// Start by clustering the devices per connections
110+
let devicesPerConnIndex = devices.reduce((memo, device) => {
109111
let qualifiedConnIndexs = chooseConns(this.conns, device);
110-
// We can not find a valid conn, just ignore this device
111112
if (qualifiedConnIndexs.length == 0) {
112113
log.error(LOG_PREFIX, 'no qualified connections for %s %s', device.appIdentifier, device.deviceToken);
113-
return Promise.resolve({
114+
let promise = Promise.resolve({
114115
transmitted: false,
115116
device: {
116117
deviceToken: device.deviceToken,
117118
deviceType: 'ios'
118119
},
119120
result: {error: 'No connection available'}
120121
});
122+
allPromises.push(promise);
123+
} else {
124+
let apnDevice = new apn.Device(device.deviceToken);
125+
apnDevice.connIndex = qualifiedConnIndexs[0];
126+
if (device.appIdentifier) {
127+
apnDevice.appIdentifier = device.appIdentifier;
128+
}
129+
memo[apnDevice.connIndex] = memo[apnDevice.connIndex] || [];
130+
memo[apnDevice.connIndex].push(apnDevice);
121131
}
122-
let conn = this.conns[qualifiedConnIndexs[0]];
123-
let apnDevice = new apn.Device(device.deviceToken);
124-
apnDevice.connIndex = qualifiedConnIndexs[0];
125-
// Add additional appIdentifier info to apn device instance
126-
if (device.appIdentifier) {
127-
apnDevice.appIdentifier = device.appIdentifier;
128-
}
129-
return new Promise((resolve, reject) => {
130-
apnDevice.callback = resolve;
131-
conn.pushNotification(notification, apnDevice);
132+
return memo;
133+
}, {})
134+
135+
allPromises = Object.keys(devicesPerConnIndex).reduce((memo, connIndex) => {
136+
let devices = devicesPerConnIndex[connIndex];
137+
// Create a promise, attach the callback
138+
let promises = devices.map((apnDevice) => {
139+
return new Promise((resolve, reject) => {
140+
apnDevice.callback = resolve;
141+
});
132142
});
133-
});
134-
return Parse.Promise.when(promises);
143+
let conn = this.conns[connIndex];
144+
conn.pushNotification(notification, devices);
145+
return memo.concat(promises);
146+
}, allPromises)
147+
148+
// let promises = devices.map((device) => {
149+
// let qualifiedConnIndexs = chooseConns(this.conns, device);
150+
// // We can not find a valid conn, just ignore this device
151+
// if (qualifiedConnIndexs.length == 0) {
152+
// log.error(LOG_PREFIX, 'no qualified connections for %s %s', device.appIdentifier, device.deviceToken);
153+
// return Promise.resolve({
154+
// transmitted: false,
155+
// device: {
156+
// deviceToken: device.deviceToken,
157+
// deviceType: 'ios'
158+
// },
159+
// result: {error: 'No connection available'}
160+
// });
161+
// }
162+
// let conn = this.conns[qualifiedConnIndexs[0]];
163+
// let apnDevice = new apn.Device(device.deviceToken);
164+
// apnDevice.connIndex = qualifiedConnIndexs[0];
165+
// // Add additional appIdentifier info to apn device instance
166+
// if (device.appIdentifier) {
167+
// apnDevice.appIdentifier = device.appIdentifier;
168+
// }
169+
// return new Promise((resolve, reject) => {
170+
// apnDevice.callback = resolve;
171+
// conn.pushNotification(notification, apnDevice);
172+
// });
173+
// });
174+
return Parse.Promise.when(allPromises);
135175
}
136176

137177
function handleTransmissionError(conns, errCode, notification, apnDevice) {

0 commit comments

Comments
 (0)