Skip to content

Amazon SNS support #711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion spec/APNS.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var APNS = require('../src/APNS');
var APNS = require('../src/APNS').APNS;

describe('APNS', () => {

Expand Down
2 changes: 1 addition & 1 deletion spec/GCM.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var GCM = require('../src/GCM');
var GCM = require('../src/GCM').GCM;

describe('GCM', () => {
it('can initialize', (done) => {
Expand Down
4 changes: 2 additions & 2 deletions spec/ParsePushAdapter.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
var ParsePushAdapter = require('../src/Adapters/Push/ParsePushAdapter');
var APNS = require('../src/APNS');
var GCM = require('../src/GCM');
var APNS = require('../src/APNS').APNS;
var GCM = require('../src/GCM').GCM;

describe('ParsePushAdapter', () => {
it('can be initialized', (done) => {
Expand Down
331 changes: 331 additions & 0 deletions spec/SNSPushAdapter.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
var SNSPushAdapter = require('../src/Adapters/Push/SNSPushAdapter');
describe('SNSPushAdapter', () => {

var pushConfig;
var snsPushAdapter;

beforeEach(function() {
pushConfig = {
pushTypes: {
ios: {ARN : "APNS_ID", production: false, bundleId: 'com.parseplatform.myapp'},
android: {ARN: "GCM_ID"}
},
accessKey: "accessKey",
secretKey: "secretKey",
region: "region"
};
snsPushAdapter = new SNSPushAdapter(pushConfig);
});

it('can be initialized', (done) => {
// Make mock config
var snsPushConfig = snsPushAdapter.snsConfig;

expect(snsPushConfig).toEqual(pushConfig.pushTypes);

done();
});

it('can get valid push types', (done) => {
expect(snsPushAdapter.getValidPushTypes()).toEqual(['ios', 'android']);
done();
});

it('can classify installation', (done) => {
// Mock installations
var validPushTypes = ['ios', 'android'];
var installations = [
{
deviceType: 'android',
deviceToken: 'androidToken'
},
{
deviceType: 'ios',
deviceToken: 'iosToken'
},
{
deviceType: 'win',
deviceToken: 'winToken'
},
{
deviceType: 'android',
deviceToken: undefined
}
];
var deviceMap = SNSPushAdapter.classifyInstallations(installations, validPushTypes);
expect(deviceMap['android']).toEqual([makeDevice('androidToken')]);
expect(deviceMap['ios']).toEqual([makeDevice('iosToken')]);
expect(deviceMap['win']).toBe(undefined);
done();
});

it('can send push notifications', (done) => {
// Mock SNS sender
var snsSender = jasmine.createSpyObj('sns', ['createPlatformEndpoint', 'publish']);
snsPushAdapter.sns = snsSender;

// Mock android ios senders
var androidSender = jasmine.createSpy('send')
var iosSender = jasmine.createSpy('send')

var senderMap = {
ios: iosSender,
android: androidSender
};
snsPushAdapter.senderMap = senderMap;

// Mock installations
var installations = [
{
deviceType: 'android',
deviceToken: 'androidToken'
},
{
deviceType: 'ios',
deviceToken: 'iosToken'
},
{
deviceType: 'win',
deviceToken: 'winToken'
},
{
deviceType: 'android',
deviceToken: undefined
}
];
var data = {};

snsPushAdapter.send(data, installations);
// Check SNS sender
expect(androidSender).toHaveBeenCalled();
var args = androidSender.calls.first().args;
expect(args[0]).toEqual(data);
expect(args[1]).toEqual([
makeDevice('androidToken')
]);
// Check ios sender
expect(iosSender).toHaveBeenCalled();
args = iosSender.calls.first().args;
expect(args[0]).toEqual(data);
expect(args[1]).toEqual([
makeDevice('iosToken')
]);
done();
});

it('can generate the right Android payload', (done) => {
var data = {"action": "com.example.UPDATE_STATUS"};
var timeStamp = 1456728000;

var returnedData = SNSPushAdapter.generateAndroidPayload(data, timeStamp);
var expectedData = {GCM: '{"priority":"normal","data":{"time":"1970-01-17T20:38:48.000Z"}}'};
expect(returnedData).toEqual(expectedData)
done();
});

it('can generate the right iOS payload', (done) => {
var data = {data : {"alert": "Check out these awesome deals!"}};
var timeStamp = 1456728000;

var returnedData = SNSPushAdapter.generateiOSPayload(data, true);
var expectedData = {APNS: '{"aps":{"alert":"Check out these awesome deals!"}}'};

var returnedData = SNSPushAdapter.generateiOSPayload(data, false);
var expectedData = {APNS_SANDBOX: '{"aps":{"alert":"Check out these awesome deals!"}}'};

expect(returnedData).toEqual(expectedData);
done();
});

it('can exchange device tokens for an Amazon Resource Number (ARN)', (done) => {
// Mock out Amazon SNS token exchange
var snsSender = jasmine.createSpyObj('sns', ['createPlatformEndpoint']);
snsPushAdapter.sns = snsSender;

// Mock installations
var installations = [
{
deviceType: 'android',
deviceToken: 'androidToken'
}
];

snsSender.createPlatformEndpoint.and.callFake(function(object, callback) {
callback(null, {'EndpointArn' : 'ARN'});
});

var promise = snsPushAdapter.exchangeTokenPromise(makeDevice("androidToken"), "GCM_ID");

promise.then(function() {
expect(snsSender.createPlatformEndpoint).toHaveBeenCalled();
var args = snsSender.createPlatformEndpoint.calls.first().args;
expect(args[0].PlatformApplicationArn).toEqual("GCM_ID");
expect(args[0].Token).toEqual("androidToken");
done();
});
});

it('can send SNS Payload', (done) => {
// Mock out Amazon SNS token exchange
var snsSender = jasmine.createSpyObj('sns', ['publish'])
snsSender.publish.and.callFake(function (object, callback) {
callback(null, '123');
});

snsPushAdapter.sns = snsSender;

// Mock installations
var installations = [
{
deviceType: 'android',
deviceToken: 'androidToken'
}
];

var promise = snsPushAdapter.sendSNSPayload("123", {"test": "hello"});

var callback = jasmine.createSpy();
promise.then(function () {
expect(snsSender.publish).toHaveBeenCalled();
var args = snsSender.publish.calls.first().args;
expect(args[0].MessageStructure).toEqual("json");
expect(args[0].TargetArn).toEqual("123");
expect(args[0].Message).toEqual('{"test":"hello"}');
done();
});
});

it('errors sending SNS Payload to Android and iOS', (done) => {
// Mock out Amazon SNS token exchange
var snsSender = jasmine.createSpyObj('sns', ['publish', 'createPlatformEndpoint']);

snsSender.createPlatformEndpoint.and.callFake(function (object, callback) {
callback("error", {});
});

snsPushAdapter.getPlatformArn(makeDevice("android"), "android", function(err, data) {
expect(err).not.toBe(null);
done();
});
});

it('can send SNS Payload to Android and iOS', (done) => {
// Mock out Amazon SNS token exchange
var snsSender = jasmine.createSpyObj('sns', ['publish', 'createPlatformEndpoint']);

snsSender.createPlatformEndpoint.and.callFake(function (object, callback) {
callback(null, {'EndpointArn': 'ARN'});
});

snsSender.publish.and.callFake(function (object, callback) {
callback(null, '123');
});

snsPushAdapter.sns = snsSender;

// Mock installations
var installations = [
{
deviceType: 'android',
deviceToken: 'androidToken'
},
{
deviceType: 'ios',
deviceToken: 'iosToken'
}
];

var promise = snsPushAdapter.send({"test": "hello"}, installations);

promise.then(function () {
expect(snsSender.publish).toHaveBeenCalled();
expect(snsSender.publish.calls.count()).toEqual(2);
done();
});
});

it('can send to APNS with known identifier', (done) => {
var snsSender = jasmine.createSpyObj('sns', ['publish', 'createPlatformEndpoint']);

snsSender.createPlatformEndpoint.and.callFake(function (object, callback) {
callback(null, {'EndpointArn': 'ARN'});
});

snsSender.publish.and.callFake(function (object, callback) {
callback(null, '123');
});

snsPushAdapter.sns = snsSender;

var promises = snsPushAdapter.sendToAPNS({"test": "hello"}, [makeDevice("ios", "com.parseplatform.myapp")]);
expect(promises.length).toEqual(1);

Promise.all(promises).then(function () {
expect(snsSender.publish).toHaveBeenCalled();
done();
});

});

it('can send to APNS with unknown identifier', (done) => {
var snsSender = jasmine.createSpyObj('sns', ['publish', 'createPlatformEndpoint']);

snsSender.createPlatformEndpoint.and.callFake(function (object, callback) {
callback(null, {'EndpointArn': 'ARN'});
});

snsSender.publish.and.callFake(function (object, callback) {
callback(null, '123');
});

snsPushAdapter.sns = snsSender;

var promises = snsPushAdapter.sendToAPNS({"test": "hello"}, [makeDevice("ios", "com.parseplatform.unknown")]);
expect(promises.length).toEqual(0);
done();
});

it('can send to APNS with multiple identifiers', (done) => {
pushConfig = {
pushTypes: {
ios: [{ARN : "APNS_SANDBOX_ID", production: false, bundleId: 'beta.parseplatform.myapp'},
{ARN : "APNS_PROD_ID", production: true, bundleId: 'com.parseplatform.myapp'}],
android: {ARN: "GCM_ID"}
},
accessKey: "accessKey",
secretKey: "secretKey",
region: "region"
};

snsPushAdapter = new SNSPushAdapter(pushConfig);

var snsSender = jasmine.createSpyObj('sns', ['publish', 'createPlatformEndpoint']);

snsSender.createPlatformEndpoint.and.callFake(function (object, callback) {
callback(null, {'EndpointArn': 'APNS_PROD_ID'});
});

snsSender.publish.and.callFake(function (object, callback) {
callback(null, '123');
});

snsPushAdapter.sns = snsSender;

var promises = snsPushAdapter.sendToAPNS({"test": "hello"}, [makeDevice("ios", "beta.parseplatform.myapp")]);
expect(promises.length).toEqual(1);
Promise.all(promises).then(function () {
expect(snsSender.publish).toHaveBeenCalled();
var args = snsSender.publish.calls.first().args[0];
expect(args.Message).toEqual("{\"APNS_SANDBOX\":\"{}\"}");
done();
});
});

function makeDevice(deviceToken, appIdentifier) {
return {
deviceToken: deviceToken,
appIdentifier: appIdentifier
};
}

});
5 changes: 2 additions & 3 deletions src/APNS.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const apn = require('apn');
* @param {String} args.bundleId The bundleId for cert
* @param {Boolean} args.production Specifies which environment to connect to: Production (if true) or Sandbox
*/
function APNS(args) {
export function APNS(args) {
// Since for ios, there maybe multiple cert/key pairs,
// typePushConfig can be an array.
let apnsArgsList = [];
Expand Down Expand Up @@ -187,7 +187,7 @@ function chooseConns(conns, device) {
* @param {Object} coreData The data field under api request body
* @returns {Object} A apns notification
*/
function generateNotification(coreData, expirationTime) {
export function generateNotification(coreData, expirationTime) {
let notification = new apn.notification();
let payload = {};
for (let key in coreData) {
Expand Down Expand Up @@ -224,4 +224,3 @@ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
APNS.chooseConns = chooseConns;
APNS.handleTransmissionError = handleTransmissionError;
}
module.exports = APNS;
4 changes: 2 additions & 2 deletions src/Adapters/Push/ParsePushAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// for ios push.

const Parse = require('parse/node').Parse;
const GCM = require('../../GCM');
const APNS = require('../../APNS');
const GCM = require('../../GCM').GCM;
const APNS = require('../../APNS').APNS;
import PushAdapter from './PushAdapter';
import { classifyInstallations } from './PushAdapterUtils';

Expand Down
Loading