Skip to content

Commit 9ebce6c

Browse files
committed
Add APNS client
1 parent 72fa1f2 commit 9ebce6c

File tree

3 files changed

+155
-1
lines changed

3 files changed

+155
-1
lines changed

APNS.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
var Parse = require('parse/node').Parse;
2+
// TODO: apn does not support the new HTTP/2 protocal. It is fine to use it in V1,
3+
// but probably we will replace it in the future.
4+
var apn = require('apn');
5+
6+
/**
7+
* Create a new connection to the APN service.
8+
* @constructor
9+
* @param {Object} args Arguments to config APNS connection
10+
* @param {String} args.cert The filename of the connection certificate to load from disk, default is cert.pem
11+
* @param {String} args.key The filename of the connection key to load from disk, default is key.pem
12+
* @param {String} args.passphrase The passphrase for the connection key, if required
13+
* @param {Boolean} args.production Specifies which environment to connect to: Production (if true) or Sandbox
14+
*/
15+
function APNS(args) {
16+
this.sender = new apn.connection(args);
17+
18+
this.sender.on('connected', function() {
19+
console.log('APNS Connected');
20+
});
21+
22+
this.sender.on('transmissionError', function(errCode, notification, device) {
23+
console.error('APNS Notification caused error: ' + errCode + ' for device ', device, notification);
24+
// TODO: For error caseud by invalid deviceToken, we should mark those installations.
25+
});
26+
27+
this.sender.on("timeout", function () {
28+
console.log("APNS Connection Timeout");
29+
});
30+
31+
this.sender.on("disconnected", function() {
32+
console.log("APNS Disconnected");
33+
});
34+
35+
this.sender.on("socketError", console.error);
36+
}
37+
38+
/**
39+
* Send apns request.
40+
* @param {Object} data The data we need to send, the format is the same with api request body
41+
* @param {Array} deviceTokens A array of device tokens
42+
* @returns {Object} A promise which is resolved immediately
43+
*/
44+
APNS.prototype.send = function(data, deviceTokens) {
45+
var coreData = data.data;
46+
var expirationTime = data['expiration_time'];
47+
var notification = generateNotification(coreData, expirationTime);
48+
this.sender.pushNotification(notification, deviceTokens);
49+
// TODO: pushNotification will push the notification to apn's queue.
50+
// We do not handle error in V1, we just relies apn to auto retry and send the
51+
// notifications.
52+
return Parse.Promise.as();
53+
}
54+
55+
/**
56+
* Generate the apns notification from the data we get from api request.
57+
* @param {Object} coreData The data field under api request body
58+
* @returns {Object} A apns notification
59+
*/
60+
var generateNotification = function(coreData, expirationTime) {
61+
var notification = new apn.notification();
62+
var payload = {};
63+
for (key in coreData) {
64+
switch (key) {
65+
case 'alert':
66+
notification.setAlertText(coreData.alert);
67+
break;
68+
case 'badge':
69+
notification.badge = coreData.badge;
70+
break;
71+
case 'sound':
72+
notification.sound = coreData.sound;
73+
break;
74+
case 'content-available':
75+
notification.setNewsstandAvailable(true);
76+
var isAvailable = coreData['content-available'] === 1;
77+
notification.setContentAvailable(isAvailable);
78+
break;
79+
case 'category':
80+
notification.category = coreData.category;
81+
break;
82+
default:
83+
payload[key] = coreData[key];
84+
break;
85+
}
86+
}
87+
notification.payload = payload;
88+
notification.expiry = expirationTime;
89+
return notification;
90+
}
91+
92+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
93+
APNS.generateNotification = generateNotification;
94+
}
95+
module.exports = APNS;

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010
"license": "BSD-3-Clause",
1111
"dependencies": {
12+
"apn": "^1.7.5",
1213
"aws-sdk": "~2.2.33",
1314
"bcrypt-nodejs": "0.0.3",
1415
"body-parser": "^1.14.2",
@@ -30,7 +31,7 @@
3031
},
3132
"scripts": {
3233
"pretest": "MONGODB_VERSION=${MONGODB_VERSION:=3.0.8} mongodb-runner start",
33-
"test": "TESTING=1 ./node_modules/.bin/istanbul cover --include-all-sources -x **/spec/** ./node_modules/.bin/jasmine",
34+
"test": "NODE_ENV=test TESTING=1 ./node_modules/.bin/istanbul cover --include-all-sources -x **/spec/** ./node_modules/.bin/jasmine",
3435
"posttest": "mongodb-runner stop",
3536
"start": "./bin/parse-server"
3637
},

spec/APNS.spec.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
var APNS = require('../APNS');
2+
3+
describe('APNS', () => {
4+
it('can generate APNS notification', (done) => {
5+
//Mock request data
6+
var data = {
7+
'alert': 'alert',
8+
'badge': 100,
9+
'sound': 'test',
10+
'content-available': 1,
11+
'category': 'INVITE_CATEGORY',
12+
'key': 'value',
13+
'keyAgain': 'valueAgain'
14+
};
15+
var expirationTime = 1454571491354
16+
17+
var notification = APNS.generateNotification(data, expirationTime);
18+
19+
expect(notification.alert).toEqual(data.alert);
20+
expect(notification.badge).toEqual(data.badge);
21+
expect(notification.sound).toEqual(data.sound);
22+
expect(notification.contentAvailable).toEqual(1);
23+
expect(notification.category).toEqual(data.category);
24+
expect(notification.payload).toEqual({
25+
'key': 'value',
26+
'keyAgain': 'valueAgain'
27+
});
28+
expect(notification.expiry).toEqual(expirationTime);
29+
done();
30+
});
31+
32+
it('can send APNS notification', (done) => {
33+
var apns = new APNS();
34+
var sender = {
35+
pushNotification: jasmine.createSpy('send')
36+
};
37+
apns.sender = sender;
38+
// Mock data
39+
var expirationTime = 1454571491354
40+
var data = {
41+
'expiration_time': expirationTime,
42+
'data': {
43+
'alert': 'alert'
44+
}
45+
}
46+
// Mock registrationTokens
47+
var deviceTokens = ['token'];
48+
49+
var promise = apns.send(data, deviceTokens);
50+
expect(sender.pushNotification).toHaveBeenCalled();
51+
var args = sender.pushNotification.calls.first().args;
52+
var notification = args[0];
53+
expect(notification.alert).toEqual(data.data.alert);
54+
expect(notification.expiry).toEqual(data['expiration_time']);
55+
expect(args[1]).toEqual(deviceTokens);
56+
done();
57+
});
58+
});

0 commit comments

Comments
 (0)