-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
808d2cc
commit 5a0a4c4
Showing
3 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
var Parse = require('parse/node').Parse; | ||
var gcm = require('node-gcm'); | ||
var randomstring = require('randomstring'); | ||
|
||
var GCMTimeToLiveMax = 4 * 7 * 24 * 60 * 60; // GCM allows a max of 4 weeks | ||
var GCMRegistrationTokensMax = 1000; | ||
|
||
function GCM(apiKey) { | ||
this.sender = new gcm.Sender(apiKey); | ||
} | ||
|
||
/** | ||
* Send gcm request. | ||
* @param {Object} data The data we need to send, the format is the same with api request body | ||
* @param {Array} registrationTokens A array of registration tokens | ||
* @returns {Object} A promise which is resolve after we get results from gcm | ||
*/ | ||
GCM.prototype.send = function (data, registrationTokens) { | ||
if (registrationTokens.length >= GCMRegistrationTokensMax) { | ||
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, | ||
'Too many registration tokens for a GCM request.'); | ||
} | ||
var pushId = randomstring.generate({ | ||
length: 10, | ||
charset: 'alphanumeric' | ||
}); | ||
var timeStamp = Date.now(); | ||
var expirationTime; | ||
if (data['expiration_time']) { | ||
expirationTime = data['expiration_time']; | ||
} | ||
// Generate gcm payload | ||
var gcmPayload = generateGCMPayload(data.data, pushId, timeStamp, expirationTime); | ||
// Make and send gcm request | ||
var message = new gcm.Message(gcmPayload); | ||
var promise = new Parse.Promise(); | ||
this.sender.send(message, { registrationTokens: registrationTokens }, 5, function (error, response) { | ||
// TODO: Use the response from gcm to generate and save push report | ||
// TODO: If gcm return some deviceTokens are invalid, set tombstone for the installation | ||
promise.resolve(); | ||
}); | ||
return promise; | ||
} | ||
|
||
/** | ||
* Generate the gcm payload from the data we get from api request. | ||
* @param {Object} data The data field under api request body | ||
* @param {String} pushId A random string | ||
* @param {Number} timeStamp A number whose format is the Unix Epoch | ||
* @param {Number|undefined} expirationTime A number whose format is the Unix Epoch or undefined | ||
* @returns {Object} A promise which is resolved after we get results from gcm | ||
*/ | ||
var generateGCMPayload = function(data, pushId, timeStamp, expirationTime) { | ||
var payloadData = { | ||
'time': timeStamp, | ||
'push_id': pushId, | ||
'data': JSON.stringify(data) | ||
} | ||
var payload = { | ||
priority: 'high', | ||
data: payloadData | ||
}; | ||
if (expirationTime) { | ||
// The timeStamp and expiration is in milliseconds but gcm requires second | ||
var timeToLive = Math.floor((expirationTime - timeStamp) / 1000); | ||
if (timeToLive < 0) { | ||
timeToLive = 0; | ||
} | ||
if (timeToLive >= GCMTimeToLiveMax) { | ||
timeToLive = GCMTimeToLiveMax; | ||
} | ||
payload.timeToLive = timeToLive; | ||
} | ||
return payload; | ||
} | ||
|
||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') { | ||
GCM.generateGCMPayload = generateGCMPayload; | ||
} | ||
module.exports = GCM; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
var GCM = require('../GCM'); | ||
|
||
describe('GCM', () => { | ||
it('can generate GCM Payload without expiration time', (done) => { | ||
//Mock request data | ||
var data = { | ||
'alert': 'alert' | ||
}; | ||
var pushId = 1; | ||
var timeStamp = 1454538822113; | ||
|
||
var payload = GCM.generateGCMPayload(data, pushId, timeStamp); | ||
|
||
expect(payload.priority).toEqual('high'); | ||
expect(payload.timeToLive).toEqual(undefined); | ||
var dataFromPayload = payload.data; | ||
expect(dataFromPayload.time).toEqual(timeStamp); | ||
expect(dataFromPayload['push_id']).toEqual(pushId); | ||
var dataFromUser = JSON.parse(dataFromPayload.data); | ||
expect(dataFromUser).toEqual(data); | ||
done(); | ||
}); | ||
|
||
it('can generate GCM Payload with valid expiration time', (done) => { | ||
//Mock request data | ||
var data = { | ||
'alert': 'alert' | ||
}; | ||
var pushId = 1; | ||
var timeStamp = 1454538822113; | ||
var expirationTime = 1454538922113 | ||
|
||
var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime); | ||
|
||
expect(payload.priority).toEqual('high'); | ||
expect(payload.timeToLive).toEqual(Math.floor((expirationTime - timeStamp) / 1000)); | ||
var dataFromPayload = payload.data; | ||
expect(dataFromPayload.time).toEqual(timeStamp); | ||
expect(dataFromPayload['push_id']).toEqual(pushId); | ||
var dataFromUser = JSON.parse(dataFromPayload.data); | ||
expect(dataFromUser).toEqual(data); | ||
done(); | ||
}); | ||
|
||
it('can generate GCM Payload with too early expiration time', (done) => { | ||
//Mock request data | ||
var data = { | ||
'alert': 'alert' | ||
}; | ||
var pushId = 1; | ||
var timeStamp = 1454538822113; | ||
var expirationTime = 1454538822112; | ||
|
||
var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime); | ||
|
||
expect(payload.priority).toEqual('high'); | ||
expect(payload.timeToLive).toEqual(0); | ||
var dataFromPayload = payload.data; | ||
expect(dataFromPayload.time).toEqual(timeStamp); | ||
expect(dataFromPayload['push_id']).toEqual(pushId); | ||
var dataFromUser = JSON.parse(dataFromPayload.data); | ||
expect(dataFromUser).toEqual(data); | ||
done(); | ||
}); | ||
|
||
it('can generate GCM Payload with too late expiration time', (done) => { | ||
//Mock request data | ||
var data = { | ||
'alert': 'alert' | ||
}; | ||
var pushId = 1; | ||
var timeStamp = 1454538822113; | ||
var expirationTime = 2454538822113; | ||
|
||
var payload = GCM.generateGCMPayload(data, pushId, timeStamp, expirationTime); | ||
|
||
expect(payload.priority).toEqual('high'); | ||
// Four week in second | ||
expect(payload.timeToLive).toEqual(4 * 7 * 24 * 60 * 60); | ||
var dataFromPayload = payload.data; | ||
expect(dataFromPayload.time).toEqual(timeStamp); | ||
expect(dataFromPayload['push_id']).toEqual(pushId); | ||
var dataFromUser = JSON.parse(dataFromPayload.data); | ||
expect(dataFromUser).toEqual(data); | ||
done(); | ||
}); | ||
|
||
it('can send GCM request', (done) => { | ||
var gcm = new GCM('apiKey'); | ||
// Mock gcm sender | ||
var sender = { | ||
send: jasmine.createSpy('send') | ||
}; | ||
gcm.sender = sender; | ||
// Mock data | ||
var expirationTime = 2454538822113; | ||
var data = { | ||
'expiration_time': expirationTime, | ||
'data': { | ||
'alert': 'alert' | ||
} | ||
} | ||
// Mock registrationTokens | ||
var registrationTokens = ['token']; | ||
|
||
var promise = gcm.send(data, registrationTokens); | ||
expect(sender.send).toHaveBeenCalled(); | ||
var args = sender.send.calls.first().args; | ||
// It is too hard to verify message of gcm library, we just verify tokens and retry times | ||
expect(args[1].registrationTokens).toEqual(registrationTokens); | ||
expect(args[2]).toEqual(5); | ||
done(); | ||
}); | ||
|
||
it('can throw on sending when we have too many registration tokens', (done) => { | ||
var gcm = new GCM('apiKey'); | ||
// Mock gcm sender | ||
var sender = { | ||
send: jasmine.createSpy('send') | ||
}; | ||
gcm.sender = sender; | ||
// Mock registrationTokens | ||
var registrationTokens = []; | ||
for (var i = 0; i <= 2000; i++) { | ||
registrationTokens.push(i.toString()); | ||
} | ||
|
||
expect(function() { | ||
gcm.send({}, registrationTokens); | ||
}).toThrow(); | ||
done(); | ||
}); | ||
}); |