Merge pull request #235 from ParsePlatform/wangmengyan.add_apns_client
Add APNS client
This commit is contained in:
95
APNS.js
Normal file
95
APNS.js
Normal file
@@ -0,0 +1,95 @@
|
||||
var Parse = require('parse/node').Parse;
|
||||
// TODO: apn does not support the new HTTP/2 protocal. It is fine to use it in V1,
|
||||
// but probably we will replace it in the future.
|
||||
var apn = require('apn');
|
||||
|
||||
/**
|
||||
* Create a new connection to the APN service.
|
||||
* @constructor
|
||||
* @param {Object} args Arguments to config APNS connection
|
||||
* @param {String} args.cert The filename of the connection certificate to load from disk, default is cert.pem
|
||||
* @param {String} args.key The filename of the connection key to load from disk, default is key.pem
|
||||
* @param {String} args.passphrase The passphrase for the connection key, if required
|
||||
* @param {Boolean} args.production Specifies which environment to connect to: Production (if true) or Sandbox
|
||||
*/
|
||||
function APNS(args) {
|
||||
this.sender = new apn.connection(args);
|
||||
|
||||
this.sender.on('connected', function() {
|
||||
console.log('APNS Connected');
|
||||
});
|
||||
|
||||
this.sender.on('transmissionError', function(errCode, notification, device) {
|
||||
console.error('APNS Notification caused error: ' + errCode + ' for device ', device, notification);
|
||||
// TODO: For error caseud by invalid deviceToken, we should mark those installations.
|
||||
});
|
||||
|
||||
this.sender.on("timeout", function () {
|
||||
console.log("APNS Connection Timeout");
|
||||
});
|
||||
|
||||
this.sender.on("disconnected", function() {
|
||||
console.log("APNS Disconnected");
|
||||
});
|
||||
|
||||
this.sender.on("socketError", console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send apns request.
|
||||
* @param {Object} data The data we need to send, the format is the same with api request body
|
||||
* @param {Array} deviceTokens A array of device tokens
|
||||
* @returns {Object} A promise which is resolved immediately
|
||||
*/
|
||||
APNS.prototype.send = function(data, deviceTokens) {
|
||||
var coreData = data.data;
|
||||
var expirationTime = data['expiration_time'];
|
||||
var notification = generateNotification(coreData, expirationTime);
|
||||
this.sender.pushNotification(notification, deviceTokens);
|
||||
// TODO: pushNotification will push the notification to apn's queue.
|
||||
// We do not handle error in V1, we just relies apn to auto retry and send the
|
||||
// notifications.
|
||||
return Parse.Promise.as();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the apns notification from the data we get from api request.
|
||||
* @param {Object} coreData The data field under api request body
|
||||
* @returns {Object} A apns notification
|
||||
*/
|
||||
var generateNotification = function(coreData, expirationTime) {
|
||||
var notification = new apn.notification();
|
||||
var payload = {};
|
||||
for (key in coreData) {
|
||||
switch (key) {
|
||||
case 'alert':
|
||||
notification.setAlertText(coreData.alert);
|
||||
break;
|
||||
case 'badge':
|
||||
notification.badge = coreData.badge;
|
||||
break;
|
||||
case 'sound':
|
||||
notification.sound = coreData.sound;
|
||||
break;
|
||||
case 'content-available':
|
||||
notification.setNewsstandAvailable(true);
|
||||
var isAvailable = coreData['content-available'] === 1;
|
||||
notification.setContentAvailable(isAvailable);
|
||||
break;
|
||||
case 'category':
|
||||
notification.category = coreData.category;
|
||||
break;
|
||||
default:
|
||||
payload[key] = coreData[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
notification.payload = payload;
|
||||
notification.expiry = expirationTime;
|
||||
return notification;
|
||||
}
|
||||
|
||||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
|
||||
APNS.generateNotification = generateNotification;
|
||||
}
|
||||
module.exports = APNS;
|
||||
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"apn": "^1.7.5",
|
||||
"aws-sdk": "~2.2.33",
|
||||
"bcrypt-nodejs": "0.0.3",
|
||||
"body-parser": "^1.14.2",
|
||||
|
||||
58
spec/APNS.spec.js
Normal file
58
spec/APNS.spec.js
Normal file
@@ -0,0 +1,58 @@
|
||||
var APNS = require('../APNS');
|
||||
|
||||
describe('APNS', () => {
|
||||
it('can generate APNS notification', (done) => {
|
||||
//Mock request data
|
||||
var data = {
|
||||
'alert': 'alert',
|
||||
'badge': 100,
|
||||
'sound': 'test',
|
||||
'content-available': 1,
|
||||
'category': 'INVITE_CATEGORY',
|
||||
'key': 'value',
|
||||
'keyAgain': 'valueAgain'
|
||||
};
|
||||
var expirationTime = 1454571491354
|
||||
|
||||
var notification = APNS.generateNotification(data, expirationTime);
|
||||
|
||||
expect(notification.alert).toEqual(data.alert);
|
||||
expect(notification.badge).toEqual(data.badge);
|
||||
expect(notification.sound).toEqual(data.sound);
|
||||
expect(notification.contentAvailable).toEqual(1);
|
||||
expect(notification.category).toEqual(data.category);
|
||||
expect(notification.payload).toEqual({
|
||||
'key': 'value',
|
||||
'keyAgain': 'valueAgain'
|
||||
});
|
||||
expect(notification.expiry).toEqual(expirationTime);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can send APNS notification', (done) => {
|
||||
var apns = new APNS();
|
||||
var sender = {
|
||||
pushNotification: jasmine.createSpy('send')
|
||||
};
|
||||
apns.sender = sender;
|
||||
// Mock data
|
||||
var expirationTime = 1454571491354
|
||||
var data = {
|
||||
'expiration_time': expirationTime,
|
||||
'data': {
|
||||
'alert': 'alert'
|
||||
}
|
||||
}
|
||||
// Mock registrationTokens
|
||||
var deviceTokens = ['token'];
|
||||
|
||||
var promise = apns.send(data, deviceTokens);
|
||||
expect(sender.pushNotification).toHaveBeenCalled();
|
||||
var args = sender.pushNotification.calls.first().args;
|
||||
var notification = args[0];
|
||||
expect(notification.alert).toEqual(data.data.alert);
|
||||
expect(notification.expiry).toEqual(data['expiration_time']);
|
||||
expect(args[1]).toEqual(deviceTokens);
|
||||
done();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user