diff --git a/spec/APNS.spec.js b/spec/APNS.spec.js index 90b485c..8f2dc5a 100644 --- a/spec/APNS.spec.js +++ b/spec/APNS.spec.js @@ -290,6 +290,18 @@ describe('APNS', () => { { deviceToken: '112233', appIdentifier: 'bundleId' + }, + { + deviceToken: '112234', + appIdentifier: 'bundleId' + }, + { + deviceToken: '112235', + appIdentifier: 'bundleId' + }, + { + deviceToken: '112236', + appIdentifier: 'bundleId' } ]; @@ -299,9 +311,94 @@ describe('APNS', () => { var notification = args[0]; expect(notification.alert).toEqual(data.data.alert); expect(notification.expiry).toEqual(data['expiration_time']/1000); - var apnDevice = args[1] - expect(apnDevice.connIndex).toEqual(0); - expect(apnDevice.appIdentifier).toEqual('bundleId'); + var apnDevices = args[1]; + apnDevices.forEach((apnDevice) => { + expect(apnDevice.connIndex).toEqual(0); + expect(apnDevice.appIdentifier).toEqual('bundleId'); + }) + done(); + }); + + it('can send APNS notification to multiple bundles', (done) => { + var args = [{ + cert: 'prodCert.pem', + key: 'prodKey.pem', + production: true, + bundleId: 'bundleId' + },{ + cert: 'devCert.pem', + key: 'devKey.pem', + production: false, + bundleId: 'bundleId.dev' + }]; + + var apns = new APNS(args); + var conn = { + pushNotification: jasmine.createSpy('send'), + bundleId: 'bundleId' + }; + var conndev = { + pushNotification: jasmine.createSpy('send'), + bundleId: 'bundleId.dev' + }; + apns.conns = [ conn, conndev ]; + // Mock data + var expirationTime = 1454571491354 + var data = { + 'expiration_time': expirationTime, + 'data': { + 'alert': 'alert' + } + } + // Mock devices + var devices = [ + { + deviceToken: '112233', + appIdentifier: 'bundleId' + }, + { + deviceToken: '112234', + appIdentifier: 'bundleId' + }, + { + deviceToken: '112235', + appIdentifier: 'bundleId' + }, + { + deviceToken: '112235', + appIdentifier: 'bundleId.dev' + }, + { + deviceToken: '112236', + appIdentifier: 'bundleId.dev' + } + ]; + + var promise = apns.send(data, devices); + + expect(conn.pushNotification).toHaveBeenCalled(); + var args = conn.pushNotification.calls.first().args; + var notification = args[0]; + expect(notification.alert).toEqual(data.data.alert); + expect(notification.expiry).toEqual(data['expiration_time']/1000); + var apnDevices = args[1]; + expect(apnDevices.length).toBe(3); + apnDevices.forEach((apnDevice) => { + expect(apnDevice.connIndex).toEqual(0); + expect(apnDevice.appIdentifier).toEqual('bundleId'); + }) + + expect(conndev.pushNotification).toHaveBeenCalled(); + args = conndev.pushNotification.calls.first().args; + notification = args[0]; + expect(notification.alert).toEqual(data.data.alert); + expect(notification.expiry).toEqual(data['expiration_time']/1000); + apnDevices = args[1]; + expect(apnDevices.length).toBe(2); + apnDevices.forEach((apnDevice) => { + expect(apnDevice.connIndex).toEqual(1); + expect(apnDevice.appIdentifier).toEqual('bundleId.dev'); + }); done(); }); }); diff --git a/src/APNS.js b/src/APNS.js index fa801a5..a5b996f 100644 --- a/src/APNS.js +++ b/src/APNS.js @@ -104,13 +104,14 @@ APNS.prototype.send = function(data, devices) { let coreData = data.data; let expirationTime = data['expiration_time']; let notification = generateNotification(coreData, expirationTime); - - let promises = devices.map((device) => { + let allPromises = []; + let devicesPerConnIndex = {}; + // Start by clustering the devices per connections + devices.forEach((device) => { let qualifiedConnIndexs = chooseConns(this.conns, device); - // We can not find a valid conn, just ignore this device if (qualifiedConnIndexs.length == 0) { log.error(LOG_PREFIX, 'no qualified connections for %s %s', device.appIdentifier, device.deviceToken); - return Promise.resolve({ + let promise = Promise.resolve({ transmitted: false, device: { deviceToken: device.deviceToken, @@ -118,20 +119,32 @@ APNS.prototype.send = function(data, devices) { }, result: {error: 'No connection available'} }); + allPromises.push(promise); + } else { + let apnDevice = new apn.Device(device.deviceToken); + apnDevice.connIndex = qualifiedConnIndexs[0]; + if (device.appIdentifier) { + apnDevice.appIdentifier = device.appIdentifier; + } + devicesPerConnIndex[apnDevice.connIndex] = devicesPerConnIndex[apnDevice.connIndex] || []; + devicesPerConnIndex[apnDevice.connIndex].push(apnDevice); } - let conn = this.conns[qualifiedConnIndexs[0]]; - let apnDevice = new apn.Device(device.deviceToken); - apnDevice.connIndex = qualifiedConnIndexs[0]; - // Add additional appIdentifier info to apn device instance - if (device.appIdentifier) { - apnDevice.appIdentifier = device.appIdentifier; - } - return new Promise((resolve, reject) => { - apnDevice.callback = resolve; - conn.pushNotification(notification, apnDevice); + }) + + allPromises = Object.keys(devicesPerConnIndex).reduce((memo, connIndex) => { + let devices = devicesPerConnIndex[connIndex]; + // Create a promise, attach the callback + let promises = devices.map((apnDevice) => { + return new Promise((resolve, reject) => { + apnDevice.callback = resolve; + }); }); - }); - return Parse.Promise.when(promises); + let conn = this.conns[connIndex]; + conn.pushNotification(notification, devices); + return memo.concat(promises); + }, allPromises); + + return Promise.all(allPromises); } function handleTransmissionError(conns, errCode, notification, apnDevice) {