From 5e74be14632ecd4727939f7aae4567d82ef44914 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:16:02 +0200 Subject: [PATCH 01/55] Removed for-sure deprecated constants --- lib/constants.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/constants.js b/lib/constants.js index 7761955..429646c 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -8,21 +8,6 @@ var Constants = { /** DEPRECATED **/ - 'TOKEN_MESSAGE_ID' : 'id', - 'TOKEN_CANONICAL_REG_ID' : 'registration_id', - 'TOKEN_ERROR' : 'Error', - 'JSON_REGISTRATION_IDS' : 'registration_ids', - 'JSON_PAYLOAD' : 'data', - 'JSON_NOTIFICATION' : 'notification', - 'JSON_SUCCESS' : 'success', - 'JSON_FAILURE' : 'failure', - 'JSON_CANONICAL_IDS' : 'canonical_ids', - 'JSON_MULTICAST_ID' : 'multicast_id', - 'JSON_RESULTS' : 'results', - 'JSON_ERROR' : 'error', - 'JSON_MESSAGE_ID' : 'message_id', - 'UTF8' : 'UTF-8', - //These errors could probably be structured more nicely, and could be used in the code. // -- maybe just as an Error abstraction? 'ERROR_QUOTA_EXCEEDED' : 'QuotaExceeded', From bde26968024dbf794382f42252cc4dfac9b4b663 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:16:29 +0200 Subject: [PATCH 02/55] Removed deprecated Result from project --- lib/node-gcm.js | 1 - lib/result.js | 13 ------------- 2 files changed, 14 deletions(-) delete mode 100644 lib/result.js diff --git a/lib/node-gcm.js b/lib/node-gcm.js index 107f64e..104ed94 100644 --- a/lib/node-gcm.js +++ b/lib/node-gcm.js @@ -6,6 +6,5 @@ exports.Constants = require('./constants'); exports.Message = require('./message'); -exports.Result = require('./result'); exports.MulitcastResult = require('./multicastresult'); exports.Sender = require('./sender'); diff --git a/lib/result.js b/lib/result.js deleted file mode 100644 index e76fedf..0000000 --- a/lib/result.js +++ /dev/null @@ -1,13 +0,0 @@ -/** DEPRECATED **/ - -function Result() { - this.messageId = undefined; - this.canonicalRegistrationId = undefined; - this.errorCode = undefined; - - console.warn("You are using node-gcm Result which is deprecated"); -} - -module.exports = Result; - -/** END DEPRECATED **/ From 3e4b168905494c014886026e778cca6c1c39e4ec Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:16:55 +0200 Subject: [PATCH 03/55] Removed deprecated MulticastResult from project --- lib/multicastresult.js | 28 ---------------------------- lib/node-gcm.js | 1 - 2 files changed, 29 deletions(-) delete mode 100644 lib/multicastresult.js diff --git a/lib/multicastresult.js b/lib/multicastresult.js deleted file mode 100644 index 8390b1c..0000000 --- a/lib/multicastresult.js +++ /dev/null @@ -1,28 +0,0 @@ -/** DEPRECATED **/ - -function MulitcastResult() { - if (!(this instanceof MulitcastResult)) { - return new MulitcastResult(); - } - - console.warn("You are using node-gcm MulticastResult, which is deprecated."); - - this.success = undefined; - this.failure = undefined; - this.canonicalIds = undefined; - this.multicastId = undefined; - this.results = []; - this.retryMulticastIds = []; -} - -MulitcastResult.prototype.addResult = function (result) { - this.results.push(result); -}; - -MulitcastResult.prototype.getTotal = function () { - return this.success + this.failure; -}; - -module.exports = MulitcastResult; - -/** END DEPRECATED **/ diff --git a/lib/node-gcm.js b/lib/node-gcm.js index 104ed94..1dfafce 100644 --- a/lib/node-gcm.js +++ b/lib/node-gcm.js @@ -6,5 +6,4 @@ exports.Constants = require('./constants'); exports.Message = require('./message'); -exports.MulitcastResult = require('./multicastresult'); exports.Sender = require('./sender'); From b1bbff038ef3f7d2394b9b2c9727a7eb670e589e Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:17:22 +0200 Subject: [PATCH 04/55] Removed comment from node-gcm.js --- lib/node-gcm.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/node-gcm.js b/lib/node-gcm.js index 1dfafce..43d1688 100644 --- a/lib/node-gcm.js +++ b/lib/node-gcm.js @@ -1,9 +1,3 @@ -/*! - * node-gcm - * Copyright(c) 2013 Marcus Farkas - * MIT Licensed - */ - exports.Constants = require('./constants'); exports.Message = require('./message'); exports.Sender = require('./sender'); From 7d34f0186c2f46d00847eb2d50ce56b906397db9 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:17:49 +0200 Subject: [PATCH 05/55] Removed deprecated methods Message#addDataWith{KeyValue,Object} from project --- lib/message.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/message.js b/lib/message.js index 2834f0c..2f10277 100644 --- a/lib/message.js +++ b/lib/message.js @@ -63,18 +63,4 @@ Message.prototype.toJson = function() { return json; }; -/** DEPRECATED */ - -Message.prototype.addDataWithKeyValue = function (key, value) { - console.warn("Message#addDataWithKeyValue has been deprecated. Please use Message#addData instead."); - this.addData(key, value); -}; - -Message.prototype.addDataWithObject = function (obj) { - console.warn("Message#addDataWithObject has been deprecated. Please use Message#addData instead."); - this.addData(obj); -}; - -/** END DEPRECATED */ - module.exports = Message; From c85f66a098b0fab1c03859ff913e5bb67dd03038 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:20:04 +0200 Subject: [PATCH 06/55] Removed tests for now-removed funcitonality --- test/unit/messageSpec.js | 83 ++-------------------------------------- 1 file changed, 3 insertions(+), 80 deletions(-) diff --git a/test/unit/messageSpec.js b/test/unit/messageSpec.js index b898fc3..7ef799b 100644 --- a/test/unit/messageSpec.js +++ b/test/unit/messageSpec.js @@ -155,83 +155,6 @@ describe('UNIT Message', function () { it.skip('should do something if not called properly'); }); - describe('addDataWithKeyValue()', function () { - it('should add properties to the message data object given a key and value', function () { - var mess = new Message(); - mess.addDataWithKeyValue('myKey', 'Message'); - - var json = mess.toJson(); - - expect(json.data.myKey).to.equal('Message'); - }); - - it('should only set values on data object, not top level message', function () { - var mess = new Message(); - mess.addDataWithKeyValue('collapseKey', 'Message'); - - var json = mess.toJson(); - - expect(json.collapse_key).to.not.equal('Message'); - expect(json.data.collapseKey).to.equal('Message'); - }); - - it.skip('should do something if not called properly'); - }); - - describe('addDataWithObject()', function () { - it('should set the data property to the object passed in', function () { - var mess = new Message(); - var obj = { - message: 'hello', - key: 'value' - }; - mess.addDataWithObject(obj); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(obj); - }); - - it('should overwrite data object when an object is passed in', function () { - var data = { - message: 'hello', - key: 'value' - }; - var mess = new Message({ data: { message: 'bye', prop: 'none' } }); - mess.addDataWithObject(data); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(data); - }); - - it('should not overwrite data if not passed an object', function () { - var data = { - message: 'hello', - key: 'value' - }; - var mess = new Message({ data: data }); - mess.addDataWithObject('adding'); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(data); - }); - - it('should not overwrite data if passed an empty object', function () { - var data = { - message: 'hello', - key: 'value' - }; - var mess = new Message({ data: data }); - mess.addDataWithObject({}); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(data); - }); - }); - describe('addNotification()', function () { it('should add attribute on notification object if pass key and value', function () { var mess = new Message(); @@ -260,7 +183,7 @@ describe('UNIT Message', function () { expect(json.notification).to.deep.equal(obj); }); }); - + describe('toJson()', function() { it('should return well-formed data for GCM if it is valid', function() { var m = new Message({ @@ -279,7 +202,7 @@ describe('UNIT Message', function () { expect(json.delayWhileIdle).to.be.an("undefined"); expect(json.dryRun).to.be.an("undefined"); }); - + it('should return well-formed data for GCM if it describes a notification', function() { var notificationData = { title: "Hello, World", @@ -296,7 +219,7 @@ describe('UNIT Message', function () { expect(json.notification).not.to.be.an("undefined"); expect(json.notification).to.deep.equal(notificationData); }); - + it('should ignore non-standard fields when serializing', function() { var m = new Message({ timeToLive: 60 * 60 * 24, From 022f6f3492cce61fc236eb5a9a951445adbd1088 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:33:45 +0200 Subject: [PATCH 07/55] Version bump and CHANGELOG for -alpha.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4171bd..41f52ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Changelog ========= +**1.0.0-alpha.0** + * Removed deprecated things: constants, Result, MulticastResult, Message#addDataWith... + **0.14.2** * Updated README, added note on v1 development diff --git a/package.json b/package.json index c4b0ad4..5ec2cb3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "node-gcm", "description": "Easy interface for Google's Cloud Messaging service (now Firebase Cloud Messaging)", - "version": "0.14.2", + "version": "1.0.0-alpha.0", "author": "Marcus Farkas ", "contributors": [ "Marcus Farkas ", From dce3ae7c2283024782f585f5a284e15502ae528b Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 12:45:58 +0200 Subject: [PATCH 08/55] Make it clear that this is v1 readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a2be20..5092bf1 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,10 @@ If you are developing an open-source project with a broader scope (like a full F See the [official FCM documentation](https://firebase.google.com/docs/cloud-messaging/) for more information. -We are currently working on version 1.0.0 of the project, and it is available in an early alpha version. +This is the README for the `v1` branch, and it is currently work in progress. +Version 1.0.0 is only available in an alpha version. Follow [PR #238](https://github.com/ToothlessGear/node-gcm/pull/238) to see current status. +We currently recommend you use the mainline version of node-gcm (found on the master branch) for production. ## Installation From 6d50454ffd1c9bf437c29f2fbd9adf07e0bd18cf Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:07:20 +0200 Subject: [PATCH 09/55] Make Sender#send{,NoRetry} expect a raw JSON object It validates and everything internally --- lib/sender.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/sender.js b/lib/sender.js index ef0af71..9d2af90 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -2,6 +2,7 @@ var Constants = require('./constants'); var _ = require('lodash'); var request = require('request'); var debug = require('debug')('node-gcm'); +var messageOptions = require("./message-options"); function Sender(key, options) { if (!(this instanceof Sender)) { @@ -168,7 +169,7 @@ Sender.prototype.sendNoRetry = function(message, recipient, callback) { }; function getRequestBody(message, recipient, callback) { - var body = message.toJson(); + var body = cleanParams(message); if(typeof recipient == "string") { body.to = recipient; @@ -201,6 +202,21 @@ function getRequestBody(message, recipient, callback) { return nextTick(callback, 'Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided!'); } +function cleanParams(raw) { + var params = {}; + Object.keys(raw).forEach(function(param) { + var paramOptions = messageOptions[param]; + if(!paramOptions) { + return console.warn("node-gcm ignored unknown message parameter " + param); + } + if(paramOptions.__argType != typeof raw[param]) { + return console.warn("node-gcm ignored wrongly typed message parameter " + param + " (was " + typeof raw[param] + ", expected " + paramOptions.__argType + ")"); + } + params[param] = raw[param]; + }); + return params; +} + function nextTick(func) { var args = Array.prototype.slice.call(arguments, 1); process.nextTick(function() { From 4e2446c2079b934c57d8bc94bf8f9bdf30790565 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:07:55 +0200 Subject: [PATCH 10/55] Remove Message from lib, no longer used --- lib/message.js | 66 ------------------------------------------------- lib/node-gcm.js | 1 - 2 files changed, 67 deletions(-) delete mode 100644 lib/message.js diff --git a/lib/message.js b/lib/message.js deleted file mode 100644 index 2f10277..0000000 --- a/lib/message.js +++ /dev/null @@ -1,66 +0,0 @@ -var messageOptions = require("./message-options"); - -function Message(raw) { - if (!(this instanceof Message)) { - return new Message(raw); - } - this.params = cleanParams(raw || {}); -} - -function cleanParams(raw) { - var params = {}; - Object.keys(raw).forEach(function(param) { - if(messageOptions[param]) { - params[param] = raw[param]; - } - }); - return params; -} - -Message.prototype.addNotification = function() { - return handleParamSet.call(this, arguments, "notification"); -}; - -function handleParamSet(args, paramType) { - if(args.length == 1) { - return setParam.call(this, paramType, args[0]); - } - if(args.length == 2) { - return addParam.call(this, paramType, args[0], args[1]); - } - throw new Error("Invalid number of arguments given to for setting " + paramType + " (" + args.length + ")"); -} - -function setParam(paramType, obj) { - if (typeof obj === 'object' && Object.keys(obj).length > 0) { - this.params[paramType] = obj; - } -} - -function addParam(paramType, key, value) { - if(!this.params[paramType]) { - this.params[paramType] = {}; - } - return this.params[paramType][key] = value; -} - -Message.prototype.addData = function() { - return handleParamSet.call(this, arguments, "data"); -}; - -Message.prototype.toJson = function() { - var json = {}; - - Object.keys(this.params).forEach(function(param) { - var optionDescription = messageOptions[param]; - if(!optionDescription) { - return; - } - var key = optionDescription.__argName || param; - json[key] = this.params[param]; - }.bind(this)); - - return json; -}; - -module.exports = Message; diff --git a/lib/node-gcm.js b/lib/node-gcm.js index 43d1688..66f310e 100644 --- a/lib/node-gcm.js +++ b/lib/node-gcm.js @@ -1,3 +1,2 @@ exports.Constants = require('./constants'); -exports.Message = require('./message'); exports.Sender = require('./sender'); From a92d7371a061df1132eafe6dc8a44b0b35ccc696 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:08:23 +0200 Subject: [PATCH 11/55] Simply export the sender --- lib/node-gcm.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/node-gcm.js b/lib/node-gcm.js index 66f310e..0c9d93c 100644 --- a/lib/node-gcm.js +++ b/lib/node-gcm.js @@ -1,2 +1 @@ -exports.Constants = require('./constants'); -exports.Sender = require('./sender'); +module.exports = require("./sender"); From ee1ce8a0c16033955c77018912aab0b76dba755b Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:09:46 +0200 Subject: [PATCH 12/55] No mapping of argument names: simply use the names that the FCM service expects --- lib/message-options.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/message-options.js b/lib/message-options.js index 1fee354..b37837e 100644 --- a/lib/message-options.js +++ b/lib/message-options.js @@ -1,43 +1,37 @@ /** * This module defines all the arguments that may be passed to a message. - * + * * Each argument may contain a field `__argName`, if the name of the field * should be different when sent to the server. - * + * * The argument may also contain a field `__argType`, if the given * argument must be of that type. The types are the strings resulting from * calling `typeof ` where `` is the argument. - * + * * Other than that, the arguments are expected to follow the indicated * structure. */ module.exports = { - collapseKey: { - __argName: "collapse_key", + collapse_key: { __argType: "string" }, priority: { __argType: "string" }, - contentAvailable: { - __argName: "content_available", + content_available: { __argType: "boolean" }, - delayWhileIdle: { - __argName: "delay_while_idle", + delay_while_idle: { __argType: "boolean" }, - timeToLive: { - __argName: "time_to_live", + time_to_live: { __argType: "number" }, - restrictedPackageName: { - __argName: "restricted_package_name", + restricted_package_name: { __argType: "string" }, - dryRun: { - __argName: "dry_run", + dry_run: { __argType: "boolean" }, data: { From b87b3ad497e48167159dd1fc4ff6e2c4aadbe285 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:10:28 +0200 Subject: [PATCH 13/55] cleaned up comment in message-options --- lib/message-options.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/message-options.js b/lib/message-options.js index b37837e..c82feee 100644 --- a/lib/message-options.js +++ b/lib/message-options.js @@ -1,15 +1,9 @@ /** * This module defines all the arguments that may be passed to a message. * - * Each argument may contain a field `__argName`, if the name of the field - * should be different when sent to the server. - * - * The argument may also contain a field `__argType`, if the given + * Each argument may contain a field `__argType`, in which case the given * argument must be of that type. The types are the strings resulting from * calling `typeof ` where `` is the argument. - * - * Other than that, the arguments are expected to follow the indicated - * structure. */ module.exports = { From 141e99860a88ad69c8b6831b46004293ce610a6a Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:11:36 +0200 Subject: [PATCH 14/55] Adjusted README to show this new reality -- much simpler! --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5092bf1..c8a0cbf 100644 --- a/README.md +++ b/README.md @@ -33,17 +33,14 @@ If you are new to GCM you should probably look into the [documentation](https:// According to below **Usage** reference, we could create such application: ```js -var gcm = require('node-gcm'); +var gcm = require('node-gcm')('YOUR_API_KEY_HERE'); -var message = new gcm.Message({ +var message = { data: { key1: 'msg1' } -}); - -// Set up the sender with you API key, prepare your recipients' registration tokens. -var sender = new gcm.Sender('YOUR_API_KEY_HERE'); +}; var regTokens = ['YOUR_REG_TOKEN_HERE']; -sender.send(message, { registrationTokens: regTokens }, function (err, response) { +gcm.send(message, { registrationTokens: regTokens }, function (err, response) { if(err) console.error(err); else console.log(response); }); From 3a5fe3f511fc7d698b72de4632e8a1e6ae8f4456 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:13:24 +0200 Subject: [PATCH 15/55] Updated the usage section to fit the real code --- README.md | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index c8a0cbf..033bf7b 100644 --- a/README.md +++ b/README.md @@ -49,21 +49,17 @@ gcm.send(message, { registrationTokens: regTokens }, function (err, response) { ## Usage ```js -var gcm = require('node-gcm'); +var gcm = require('node-gcm')('insert Google Server API Key here'); -// Create a message -// ... with default values -var message = new gcm.Message(); - -// ... or some given values -var message = new gcm.Message({ - collapseKey: 'demo', +// Create a message (all possible values shown) +var message = { + collapse_key: 'demo', priority: 'high', - contentAvailable: true, - delayWhileIdle: true, - timeToLive: 3, - restrictedPackageName: "somePackageName", - dryRun: true, + content_available: true, + delay_while_idle: true, + time_to_live: 3, + restricted_package_name: "somePackageName", + dry_run: true, data: { key1: 'message1', key2: 'message2' @@ -73,21 +69,7 @@ var message = new gcm.Message({ icon: "ic_launcher", body: "This is a notification that will be displayed ASAP." } -}); - -// Change the message data -// ... as key-value -message.addData('key1','message1'); -message.addData('key2','message2'); - -// ... or as a data object (overwrites previous data object) -message.addData({ - key1: 'message1', - key2: 'message2' -}); - -// Set up the sender with you API key -var sender = new gcm.Sender('insert Google Server API Key here'); +}; // Add the registration tokens of the devices you want to send to var registrationTokens = []; @@ -96,23 +78,24 @@ registrationTokens.push('regToken2'); // Send the message // ... trying only once -sender.sendNoRetry(message, { registrationTokens: registrationTokens }, function(err, response) { +gcm.sendNoRetry(message, { registrationTokens: registrationTokens }, function(err, response) { if(err) console.error(err); else console.log(response); }); // ... or retrying -sender.send(message, { registrationTokens: registrationTokens }, function (err, response) { +gcm.send(message, { registrationTokens: registrationTokens }, function (err, response) { if(err) console.error(err); else console.log(response); }); // ... or retrying a specific number of times (10) -sender.send(message, { registrationTokens: registrationTokens }, 10, function (err, response) { +gcm.send(message, { registrationTokens: registrationTokens }, 10, function (err, response) { if(err) console.error(err); else console.log(response); }); ``` + ## Recipients You can send push notifications to various recipient types by providing one of the following recipient keys: From a3dcac0ae48b8c5058031a5beb3dd18fbfc930b8 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:15:30 +0200 Subject: [PATCH 16/55] More adjustments to the README --- README.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 033bf7b..f421c8b 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,6 @@ gcm.send(message, { registrationTokens: registrationTokens }, 10, function (err, You can send push notifications to various recipient types by providing one of the following recipient keys: - |Key|Type|Description| |---|---|---| |to|String|A single [registration token](https://developers.google.com/cloud-messaging/android/client#sample-register), [notification key](https://developers.google.com/cloud-messaging/notifications), or [topic](https://developers.google.com/cloud-messaging/topic-messaging). @@ -117,21 +116,13 @@ This is due to [a restriction](http://developer.android.com/training/cloudsync/g ## Notification usage ```js - -var message = new gcm.Message(); - -// Add notification payload as key value -message.addNotification('title', 'Alert!!!'); -message.addNotification('body', 'Abnormal data access'); -message.addNotification('icon', 'ic_launcher'); - -// as object -message.addNotification({ - title: 'Alert!!!', - body: 'Abnormal data access', - icon: 'ic_launcher' -}); - +var message = { + notification: { + title: 'Alert!!!', + body: 'Abnormal data access', + icon: 'ic_launcher' + } +}; ``` ### Notification payload option table @@ -164,13 +155,13 @@ var requestOptions = { timeout: 5000 }; -// Set up the sender with your API key and request options -var sender = new gcm.Sender('YOUR_API_KEY_HERE', requestOptions); +// Set up gcm with your API key and request options +var gcm = require("node-gcm")('YOUR_API_KEY_HERE', requestOptions); // Prepare a GCM message... // Send it to GCM endpoint with modified request options -sender.send(message, { registrationTokens: regTokens }, function (err, response) { +gcm.send(message, { registrationTokens: regTokens }, function (err, response) { if(err) console.error(err); else console.log(response); }); From 8f93ad811c3a5264b6539e0fb742e542938effeb Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:17:06 +0200 Subject: [PATCH 17/55] Updated example to new reality --- examples/notification.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/notification.js b/examples/notification.js index 11ec000..19cb348 100644 --- a/examples/notification.js +++ b/examples/notification.js @@ -1,19 +1,21 @@ -var gcm = require('../lib/node-gcm'); - -var message = new gcm.Message(); - -message.addData('hello', 'world'); -message.addNotification('title', 'Hello'); -message.addNotification('icon', 'ic_launcher'); -message.addNotification('body', 'World'); +//Replace your developer API key with GCM enabled here +var gcm = require('../index')('AIza*******************5O6FM'); +var message = { + data: { + hello: 'world' + }, + notification: { + title: 'Hello', + icon: 'ic_launcher', + body: 'World' + } +}; //Add your mobile device registration tokens here var regTokens = ['ecG3ps_bNBk:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXl7TDJkW']; -//Replace your developer API key with GCM enabled here -var sender = new gcm.Sender('AIza*******************5O6FM'); -sender.send(message, regTokens, function (err, response) { +gcm.send(message, regTokens, function (err, response) { if(err) { console.error(err); } else { From 9a4b643604e0ee09fa8fb101c264f0ba2f9b35e1 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:47:29 +0200 Subject: [PATCH 18/55] Adjust sender specs to new reality --- test/unit/senderSpec.js | 114 +++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 60 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index e736848..ed2ad4d 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -4,9 +4,7 @@ var chai = require('chai'), expect = chai.expect, sinon = require('sinon'), proxyquire = require('proxyquire'), - senderPath = '../../lib/sender', - Constants = require('../../lib/constants'), - Message = require('../../lib/message'); + senderPath = '../../lib/sender'; describe('UNIT Sender', function () { // Use object to set arguments passed into callback @@ -18,12 +16,12 @@ describe('UNIT Sender', function () { var Sender = proxyquire(senderPath, { 'request': requestStub }); describe('constructor', function () { - var Sender = require(senderPath); + var gcm = require(senderPath); it('should call new on constructor if user does not', function () { - var sender = Sender(); + var sender = gcm(); expect(sender).to.not.be.undefined; - expect(sender).to.be.instanceOf(Sender); + expect(sender).to.be.instanceOf(gcm); }); it('should create a Sender with key and options passed in', function () { @@ -33,13 +31,11 @@ describe('UNIT Sender', function () { timeout: 100 }; var key = 'myAPIKey', - sender = new Sender(key, options); - expect(sender).to.be.instanceOf(Sender); + sender = new gcm(key, options); + expect(sender).to.be.instanceOf(gcm); expect(sender.key).to.equal(key); expect(sender.options).to.deep.equal(options); }); - - it.skip('should do something if not passed a valid key'); }); describe('sendNoRetry()', function () { @@ -62,7 +58,7 @@ describe('UNIT Sender', function () { strictSSL: false }; var sender = new Sender('mykey', options); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', function () {}); setTimeout(function() { expect(args.options.proxy).to.equal(options.proxy); @@ -83,7 +79,7 @@ describe('UNIT Sender', function () { json: { test: true } }; var sender = new Sender('mykey', options); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', function () {}); setTimeout(function() { expect(args.options.method).to.not.equal(options.method); @@ -101,7 +97,7 @@ describe('UNIT Sender', function () { } }; var sender = new Sender('mykey', options); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', function () {}); setTimeout(function() { expect(args.options.headers.Authorization).to.not.equal(options.headers.Auhtorization); @@ -116,7 +112,7 @@ describe('UNIT Sender', function () { } }; var sender = new Sender('mykey', options); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', function () {}); setTimeout(function() { expect(args.options.headers.Custom).to.deep.equal(options.headers.Custom); @@ -131,7 +127,7 @@ describe('UNIT Sender', function () { timeout: 1000 }; var sender = new Sender('mykey', options); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', function () {}); setTimeout(function() { expect(args.options.strictSSL).to.be.an('undefined'); @@ -141,7 +137,7 @@ describe('UNIT Sender', function () { it('should set the API key of req object if passed in API key', function (done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', function () {}); setTimeout(function() { expect(args.options.headers.Authorization).to.equal('key=myKey'); @@ -151,7 +147,7 @@ describe('UNIT Sender', function () { it('should send a JSON object as the body of the request', function (done) { var sender = new Sender('mykey'); - var m = new Message({ collapseKey: 'Message', data: {} }); + var m = { collapseKey: 'Message', data: {} }; sender.sendNoRetry(m, '', function () {}); setTimeout(function() { expect(args.options.json).to.be.a('object'); @@ -160,31 +156,31 @@ describe('UNIT Sender', function () { }); it('should set properties of body with message properties', function (done) { - var mess = new Message({ - delayWhileIdle: true, - collapseKey: 'Message', - timeToLive: 100, - dryRun: true, + var mess = { + delay_while_idle: true, + collapse_key: 'Message', + time_to_live: 100, + dry_run: true, data: { name: 'Matt' } - }); + }; var sender = new Sender('mykey'); sender.sendNoRetry(mess, '', function () {}); setTimeout(function() { var body = args.options.json; - expect(body[Constants.PARAM_DELAY_WHILE_IDLE]).to.equal(mess.delayWhileIdle); - expect(body[Constants.PARAM_COLLAPSE_KEY]).to.equal(mess.collapseKey); - expect(body[Constants.PARAM_TIME_TO_LIVE]).to.equal(mess.timeToLive); - expect(body[Constants.PARAM_DRY_RUN]).to.equal(mess.dryRun); - expect(body[Constants.PARAM_PAYLOAD_KEY]).to.deep.equal(mess.data); + expect(body.delay_while_idle).to.equal(mess.delay_while_idle); + expect(body.collapse_key).to.equal(mess.collapse_key); + expect(body.time_to_live).to.equal(mess.time_to_live); + expect(body.dry_run).to.equal(mess.dry_run); + expect(body.data).to.deep.equal(mess.data); done(); }, 10); }); it('should set the registration_ids to reg tokens implicitly passed in', function (done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, ["registration token 1", "registration token 2"], function () {}); setTimeout(function() { var body = args.options.json; @@ -195,7 +191,7 @@ describe('UNIT Sender', function () { it('should set the registration_ids to reg tokens explicitly passed in', function (done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var regTokens = ["registration token 1", "registration token 2"]; sender.sendNoRetry(m, { registrationIds: regTokens }, function () {}); setTimeout(function() { @@ -207,7 +203,7 @@ describe('UNIT Sender', function () { it('should set the registration_ids to reg tokens explicitly passed in', function (done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var regTokens = ["registration token 1", "registration token 2"]; sender.sendNoRetry(m, { registrationTokens: regTokens }, function () {}); setTimeout(function() { @@ -219,7 +215,7 @@ describe('UNIT Sender', function () { it('should set the to field if a single reg (or other) token is passed in', function(done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, "registration token 1", function () {}); setTimeout(function() { var body = args.options.json; @@ -231,7 +227,7 @@ describe('UNIT Sender', function () { it('should set the to field if a single reg token is passed in as a string', function(done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var token = "registration token 1"; sender.sendNoRetry(m, token, function () {}); setTimeout(function() { @@ -244,7 +240,7 @@ describe('UNIT Sender', function () { it('should set the to field if a single reg token is passed inside the recipient array', function(done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var token = "registration token 1"; sender.sendNoRetry(m, [ token ], function () {}); setTimeout(function() { @@ -257,7 +253,7 @@ describe('UNIT Sender', function () { it('should set the to field if a single reg token is passed inside the registrationTokens array', function(done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var token = "registration token 1"; sender.sendNoRetry(m, { registrationTokens: token }, function () {}); setTimeout(function() { @@ -270,7 +266,7 @@ describe('UNIT Sender', function () { it('should set the to field if a single reg token is passed inside the registrationIDs array', function(done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var token = "registration token 1"; sender.sendNoRetry(m, { registrationIDs: token }, function () {}); setTimeout(function() { @@ -283,7 +279,7 @@ describe('UNIT Sender', function () { it('should set the to field if a topic is passed in', function(done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var topic = '/topics/tests'; sender.sendNoRetry(m, { topic: topic }, function () {}); setTimeout(function() { @@ -296,7 +292,7 @@ describe('UNIT Sender', function () { it('should set the to field if a to recipient is passed in', function(done) { var sender = new Sender('myKey'); - var m = new Message({ data: {} }); + var m = { data: {} }; var token = "registration token 1"; sender.sendNoRetry(m, { to: token }, function () {}); setTimeout(function() { @@ -310,7 +306,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if recipient is an empty object', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {}, callback); + sender.sendNoRetry({}, {}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -321,7 +317,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if recipient keys are invalid', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {invalid: true}, callback); + sender.sendNoRetry({}, {invalid: true}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -332,7 +328,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if provided more than one recipient key', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {registrationIds: ['string'], topic: 'string'}, callback); + sender.sendNoRetry({}, {registrationIds: ['string'], topic: 'string'}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -343,7 +339,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if registrationIds is not an array', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {registrationIds: 'string'}, callback); + sender.sendNoRetry({}, {registrationIds: 'string'}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -354,7 +350,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if registrationTokens is not an array', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {registrationTokens: 'string'}, callback); + sender.sendNoRetry({}, {registrationTokens: 'string'}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -365,7 +361,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if to is not a string', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {to: ['array']}, callback); + sender.sendNoRetry({}, {to: ['array']}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -376,7 +372,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if topic is not a string', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {topic: ['array']}, callback); + sender.sendNoRetry({}, {topic: ['array']}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -387,7 +383,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if notificationKey is not a string', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {notificationKey: ['array']}, callback); + sender.sendNoRetry({}, {notificationKey: ['array']}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -398,7 +394,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if to is empty', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {to: ''}, callback); + sender.sendNoRetry({}, {to: ''}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -409,7 +405,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if topic is empty', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {topic: ''}, callback); + sender.sendNoRetry({}, {topic: ''}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -420,7 +416,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if notificationKey is empty', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {notificationKey: ''}, callback); + sender.sendNoRetry({}, {notificationKey: ''}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -431,7 +427,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if no recipient provided', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry(new Message(), {}, callback); + sender.sendNoRetry({}, {}, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -443,7 +439,7 @@ describe('UNIT Sender', function () { var callback = sinon.spy(), sender = new Sender('myKey'); setArgs('an error', {}, {}); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; @@ -456,7 +452,7 @@ describe('UNIT Sender', function () { var callback = sinon.spy(), sender = new Sender('myKey'); setArgs(null, { statusCode: 500 }, {}); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; @@ -469,7 +465,7 @@ describe('UNIT Sender', function () { var callback = sinon.spy(), sender = new Sender('myKey'); setArgs(null, { statusCode: 401 }, {}); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; @@ -482,7 +478,7 @@ describe('UNIT Sender', function () { var callback = sinon.spy(), sender = new Sender('myKey'); setArgs(null, { statusCode: 400 }, {}); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; @@ -496,7 +492,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'), parseError = {error: 'Failed to parse JSON'}; setArgs(parseError, null, null); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; @@ -513,7 +509,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('myKey'); setArgs(null, { statusCode: 200 }, resBody); - var m = new Message({ data: {} }); + var m = { data: {} }; sender.sendNoRetry(m, '', callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; @@ -524,8 +520,7 @@ describe('UNIT Sender', function () { }); describe('send()', function () { - var restore = {}, - backoff = Constants.BACKOFF_INITIAL_DELAY; + var restore = {}; // Set args passed into sendNoRetry function setArgs(err, response) { args = { @@ -538,7 +533,6 @@ describe('UNIT Sender', function () { before( function () { restore.sendNoRetry = Sender.prototype.sendNoRetry; Sender.prototype.sendNoRetry = function (message, reg_tokens, callback) { - console.log('Firing send'); args.message = message; args.reg_tokens = reg_tokens; args.tries++; @@ -665,7 +659,7 @@ describe('UNIT Sender', function () { var start = new Date(); var callback = function () { expect(args.tries).to.equal(2); - expect(new Date() - start).to.be.gte(Math.pow(2, 0) * backoff); + expect(new Date() - start).to.be.gte(1000); done(); }; var sender = new Sender('myKey'); From 93aa28d9a5756592f5a12dd7f5bce61bec924fbc Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:49:22 +0200 Subject: [PATCH 19/55] Add a test that invalid or wrong properties are ignored --- test/unit/senderSpec.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index ed2ad4d..06ace67 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -178,6 +178,31 @@ describe('UNIT Sender', function () { }, 10); }); + it('should ignore properties of body that are unknown or invalid types', function(done) { + var mess = { + delay_while_idle: "a string", + collapse_key: true, + time_to_live: 100, + dry_run: true, + data: { + name: 'Matt' + }, + unknown_property: "hello" + }; + var sender = new Sender('mykey'); + sender.sendNoRetry(mess, '', function () {}); + setTimeout(function() { + var body = args.options.json; + expect(body.delay_while_idle).to.equal(undefined); + expect(body.collapse_key).to.equal(undefined); + expect(body.time_to_live).to.equal(mess.time_to_live); + expect(body.dry_run).to.equal(mess.dry_run); + expect(body.data).to.deep.equal(mess.data); + expect(body.unknown_property).to.equal(undefined); + done(); + }, 10); + }); + it('should set the registration_ids to reg tokens implicitly passed in', function (done) { var sender = new Sender('myKey'); var m = { data: {} }; From f3208cf7d5aff41f3acd66b2ba224362bdc5c92c Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 15:49:36 +0200 Subject: [PATCH 20/55] Remove message specs, no longer needed What they covered before is now covered by sender --- test/unit/messageSpec.js | 237 --------------------------------------- 1 file changed, 237 deletions(-) delete mode 100644 test/unit/messageSpec.js diff --git a/test/unit/messageSpec.js b/test/unit/messageSpec.js deleted file mode 100644 index 7ef799b..0000000 --- a/test/unit/messageSpec.js +++ /dev/null @@ -1,237 +0,0 @@ -"use strict"; - -var Message = require('../../lib/message'), - chai = require('chai'), - expect = chai.expect; - -describe('UNIT Message', function () { - describe('constructor', function () { - it('can be instantiated with no state', function () { - var mess = new Message(); - var json = mess.toJson(); - expect(json).to.deep.equal({}); - }); - - it('should call new on constructor if user does not', function () { - var mess = Message(); - expect(mess).to.not.be.an("undefined"); - expect(mess).to.be.instanceOf(Message); - }); - - it('should create an message with properties passed in', function () { - var obj = { - collapseKey: 'Message', - delayWhileIdle: true, - timeToLive: 100, - dryRun: true, - priority: 'high', - contentAvailable: false, - restrictedPackageName: "com.example.App", - data: { - score: 98 - }, - notification: {} - }; - var mess = new Message(obj); - - var json = mess.toJson(); - - expect(json).to.deep.equal({ - collapse_key: "Message", - delay_while_idle: true, - time_to_live: 100, - dry_run: true, - priority: 'high', - content_available: false, - restricted_package_name: "com.example.App", - data: { - score: 98 - }, - notification: {} - }); - }); - - it('should only set properties passed into constructor', function () { - var obj = { - collapseKey: 'Message', - delayWhileIdle: true, - data: { - score: 98 - }, - notification: {} - }; - var mess = new Message(obj); - - var json = mess.toJson(); - - expect(json).to.deep.equal({ - collapse_key: "Message", - delay_while_idle: true, - data: { - score: 98 - }, - notification: {} - }); - expect(json.time_to_live).to.be.an("undefined"); - expect(json.dry_run).to.be.an("undefined"); - expect(json.priority).to.be.an("undefined"); - expect(json.content_available).to.be.an("undefined"); - expect(json.restricted_package_name).to.be.an("undefined"); - }); - }); - - describe('addData()', function () { - it('should add properties to the message data object given a key and value', function () { - var mess = new Message(); - mess.addData('myKey', 'Message'); - - var json = mess.toJson(); - - expect(json.data.myKey).to.equal('Message'); - }); - - it('should only set values on data object, not top level message', function () { - var mess = new Message(); - mess.addData('collapseKey', 'Message'); - - var json = mess.toJson(); - - expect(json.collapse_key).to.not.equal('Message'); - expect(json.data.collapseKey).to.equal('Message'); - }); - - it('should set the data property to the object passed in', function () { - var mess = new Message(); - var obj = { - message: 'hello', - key: 'value' - }; - mess.addData(obj); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(obj); - }); - - it('should overwrite data object when an object is passed in', function () { - var data = { - message: 'hello', - key: 'value' - }; - var mess = new Message({ data: { message: 'bye', prop: 'none' } }); - mess.addData(data); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(data); - }); - - it('should not overwrite data if not passed an object', function () { - var data = { - message: 'hello', - key: 'value' - }; - var mess = new Message({ data: data }); - mess.addData('adding'); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(data); - }); - - it('should not overwrite data if passed an empty object', function () { - var data = { - message: 'hello', - key: 'value' - }; - var mess = new Message({ data: data }); - mess.addData({}); - - var json = mess.toJson(); - - expect(json.data).to.deep.equal(data); - }); - - it.skip('should do something if not called properly'); - }); - - describe('addNotification()', function () { - it('should add attribute on notification object if pass key and value', function () { - var mess = new Message(); - mess.addNotification('title', 'hello'); - mess.addNotification('icon', 'ic_launcher'); - mess.addNotification('body', 'world'); - - var json = mess.toJson(); - - expect(json.notification.title).to.equal('hello'); - expect(json.notification.icon).to.equal('ic_launcher'); - expect(json.notification.body).to.equal('world'); - }); - - it('should set the notification property to the object passed in', function () { - var mess = new Message(); - var obj = { - title: 'hello', - icon: 'ic_launcher', - body: 'world' - }; - mess.addNotification(obj); - - var json = mess.toJson(); - - expect(json.notification).to.deep.equal(obj); - }); - }); - - describe('toJson()', function() { - it('should return well-formed data for GCM if it is valid', function() { - var m = new Message({ - delayWhileIdle: true, - dryRun: true, - data: { - hello: "world" - } - }); - - var json = m.toJson(); - - expect(json.delay_while_idle).to.equal(true); - expect(json.dry_run).to.equal(true); - expect(json.data.hello).to.equal("world"); - expect(json.delayWhileIdle).to.be.an("undefined"); - expect(json.dryRun).to.be.an("undefined"); - }); - - it('should return well-formed data for GCM if it describes a notification', function() { - var notificationData = { - title: "Hello, World", - icon: 'ic_launcher', - body: "This is a quick notification." - }; - - var m = new Message({ delayWhileIdle: true }); - m.addNotification(notificationData); - - var json = m.toJson(); - - expect(json.delay_while_idle).to.equal(true); - expect(json.notification).not.to.be.an("undefined"); - expect(json.notification).to.deep.equal(notificationData); - }); - - it('should ignore non-standard fields when serializing', function() { - var m = new Message({ - timeToLive: 60 * 60 * 24, - wrongField: "should be excluded", - alsoThisFieldIsWrong: "and should not show up" - }); - - var json = m.toJson(); - - expect(json.time_to_live).to.equal(60 * 60 * 24); - expect(Object.keys(json).length).to.equal(1); - }); - }); - -}); From 3784ce433800c81b55e9fa57b6fa4064d142d80f Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Sun, 29 May 2016 16:29:06 +0200 Subject: [PATCH 21/55] Depend only on lodash.defaultsdeep (instead of all lodash) This makes for a significantly smaller dependency tree (lodash is 2Mb, lodash.defaultsdeep is around 250kb) --- lib/sender.js | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index ef0af71..0679df0 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -1,5 +1,5 @@ var Constants = require('./constants'); -var _ = require('lodash'); +var defaultsDeep = require('lodash.defaultsdeep'); var request = require('request'); var debug = require('debug')('node-gcm'); @@ -135,7 +135,7 @@ Sender.prototype.sendNoRetry = function(message, recipient, callback) { } //Build request options, allowing some to be overridden - var request_options = _.defaultsDeep({ + var request_options = defaultsDeep({ method: 'POST', headers: { 'Authorization': 'key=' + this.key diff --git a/package.json b/package.json index 5ec2cb3..303e167 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ }, "dependencies": { "debug": "^0.8.1", - "lodash": "^3.10.1", + "lodash.defaultsdeep": "^4.4.0", "request": "^2.27.0" }, "devDependencies": { From 022ce33c705afc4cf73deaecec59ce4488069c36 Mon Sep 17 00:00:00 2001 From: Elad Nava Date: Mon, 30 May 2016 11:45:03 +0300 Subject: [PATCH 22/55] Add entries to .gitignore (vscode & test.js) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index aedd71b..350ba7d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ pids logs results +test.js + node_modules npm-debug.log .idea +.vscode From 8160865bb03282c34ddb0a42c372fcceee31a325 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Mon, 30 May 2016 18:16:45 +0200 Subject: [PATCH 23/55] Only allow arrays or strings as recipients -- move closer to the API interface Remove unnecessary logic :-) Now we just do what the HTTP interface makes natural: a single recipient goes in the `to` field; multiple recipients go in the `registration_ids` field --- lib/sender.js | 63 +-------------------------------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index b81be74..4cca617 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -176,30 +176,10 @@ function getRequestBody(message, recipient, callback) { return nextTick(callback, null, body); } if(Array.isArray(recipient)) { - if(!recipient.length) { - return nextTick(callback, 'No recipient provided!'); - } - else if(recipient.length == 1) { - body.to = recipient[0]; - return nextTick(callback, null, body); - } body.registration_ids = recipient; return nextTick(callback, null, body); } - if (typeof recipient == "object") { - return extractRecipient(recipient, function(err, recipient) { - if(err) { - return callback(err); - } - if (Array.isArray(recipient)) { - body.registration_ids = recipient; - return callback(null, body); - } - body.to = recipient; - return callback(null, body); - }); - } - return nextTick(callback, 'Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided!'); + return nextTick(callback, 'Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!'); } function cleanParams(raw) { @@ -224,45 +204,4 @@ function nextTick(func) { }.bind(this)); } -function extractRecipient(recipient, callback) { - var recipientKeys = Object.keys(recipient); - - if(recipientKeys.length !== 1) { - return nextTick(callback, new Error("Please specify exactly one recipient key (you specified [" + recipientKeys + "])")); - } - - var key = recipientKeys[0]; - var value = recipient[key]; - - if(!value) { - return nextTick(callback, new Error("Falsy value for recipient key '" + key + "'.")); - } - - var keyValidators = { - to: isString, - topic: isString, - notificationKey: isString, - registrationIds: isArray, - registrationTokens: isArray - }; - - var validator = keyValidators[key]; - if(!validator) { - return nextTick(callback, new Error("Key '" + key + "' is not a valid recipient key.")); - } - if(!validator(value)) { - return nextTick(callback, new Error("Recipient key '" + key + "' was provided as an incorrect type.")); - } - - return nextTick(callback, null, value); -} - -function isString(x) { - return typeof x == "string"; -} - -function isArray(x) { - return Array.isArray(x); -} - module.exports = Sender; From 976a02c3ff8db70486c7ec673773206d6fef8a3d Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Mon, 30 May 2016 18:35:59 +0200 Subject: [PATCH 24/55] Still check if the array is empty, though --- lib/sender.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/sender.js b/lib/sender.js index 4cca617..091dac9 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -176,6 +176,9 @@ function getRequestBody(message, recipient, callback) { return nextTick(callback, null, body); } if(Array.isArray(recipient)) { + if(recipient.length < 1) { + return nextTick(callback, 'Empty recipient array passed!'); + } body.registration_ids = recipient; return nextTick(callback, null, body); } From ea0a850ba3a78f32ef78171c3f8a81048d37d84c Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Mon, 30 May 2016 18:36:51 +0200 Subject: [PATCH 25/55] Return error objects instead of strings --- lib/sender.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 091dac9..1fd87ed 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -177,12 +177,12 @@ function getRequestBody(message, recipient, callback) { } if(Array.isArray(recipient)) { if(recipient.length < 1) { - return nextTick(callback, 'Empty recipient array passed!'); + return nextTick(callback, new Error('Empty recipient array passed!')); } body.registration_ids = recipient; return nextTick(callback, null, body); } - return nextTick(callback, 'Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!'); + return nextTick(callback, new Error('Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!')); } function cleanParams(raw) { From 9ab4d23f3ade22931c0bb4d3089a7581c38ec504 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Mon, 30 May 2016 18:37:28 +0200 Subject: [PATCH 26/55] Remove all the specs that had to do with the special recipient formattings! --- test/unit/senderSpec.js | 170 +--------------------------------------- 1 file changed, 4 insertions(+), 166 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 06ace67..90312a5 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -263,67 +263,15 @@ describe('UNIT Sender', function () { }, 10); }) - it('should set the to field if a single reg token is passed inside the recipient array', function(done) { + it('should set the registration_id field if a single reg token is passed inside the recipient array', function(done) { var sender = new Sender('myKey'); var m = { data: {} }; var token = "registration token 1"; sender.sendNoRetry(m, [ token ], function () {}); setTimeout(function() { var body = args.options.json; - expect(body.to).to.deep.equal(token); - expect(body.registration_ids).to.be.an("undefined"); - done(); - }, 10); - }) - - it('should set the to field if a single reg token is passed inside the registrationTokens array', function(done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var token = "registration token 1"; - sender.sendNoRetry(m, { registrationTokens: token }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.to).to.deep.equal(token); - expect(body.registration_ids).to.be.an("undefined"); - done(); - }, 10); - }) - - it('should set the to field if a single reg token is passed inside the registrationIDs array', function(done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var token = "registration token 1"; - sender.sendNoRetry(m, { registrationIDs: token }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.to).to.deep.equal(token); - expect(body.registration_ids).to.be.an("undefined"); - done(); - }, 10); - }) - - it('should set the to field if a topic is passed in', function(done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var topic = '/topics/tests'; - sender.sendNoRetry(m, { topic: topic }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.to).to.deep.equal(topic); - expect(body.registration_ids).to.be.an("undefined"); - done(); - }, 10); - }) - - it('should set the to field if a to recipient is passed in', function(done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var token = "registration token 1"; - sender.sendNoRetry(m, { to: token }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.to).to.deep.equal(token); - expect(body.registration_ids).to.be.an("undefined"); + expect(body.registration_ids).to.deep.equal([ token ]); + expect(body.to).to.be.an("undefined"); done(); }, 10); }) @@ -339,120 +287,10 @@ describe('UNIT Sender', function () { }, 10); }); - it('should pass an error into callback if recipient keys are invalid', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {invalid: true}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if provided more than one recipient key', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {registrationIds: ['string'], topic: 'string'}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if registrationIds is not an array', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {registrationIds: 'string'}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if registrationTokens is not an array', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {registrationTokens: 'string'}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if to is not a string', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {to: ['array']}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if topic is not a string', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {topic: ['array']}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if notificationKey is not a string', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {notificationKey: ['array']}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if to is empty', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {to: ''}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if topic is empty', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {topic: ''}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - - it('should pass an error into callback if notificationKey is empty', function (done) { - var callback = sinon.spy(); - var sender = new Sender('myKey'); - sender.sendNoRetry({}, {notificationKey: ''}, callback); - setTimeout(function() { - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.be.a('object'); - done(); - }, 10); - }); - it('should pass an error into callback if no recipient provided', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry({}, {}, callback); + sender.sendNoRetry({}, [], callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); From e62aaae427018f34072f93ef18418f6bbc5196ab Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Mon, 30 May 2016 19:02:22 +0200 Subject: [PATCH 27/55] Update README to match new interface --- README.md | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f421c8b..a6fb66d 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,9 @@ var gcm = require('node-gcm')('YOUR_API_KEY_HERE'); var message = { data: { key1: 'msg1' } }; -var regTokens = ['YOUR_REG_TOKEN_HERE']; +var recipient = 'YOUR_REG_TOKEN_HERE'; -gcm.send(message, { registrationTokens: regTokens }, function (err, response) { +gcm.send(message, recipient, function (err, response) { if(err) console.error(err); else console.log(response); }); @@ -78,19 +78,19 @@ registrationTokens.push('regToken2'); // Send the message // ... trying only once -gcm.sendNoRetry(message, { registrationTokens: registrationTokens }, function(err, response) { +gcm.sendNoRetry(message, registrationTokens, function(err, response) { if(err) console.error(err); else console.log(response); }); // ... or retrying -gcm.send(message, { registrationTokens: registrationTokens }, function (err, response) { +gcm.send(message, registrationTokens, function (err, response) { if(err) console.error(err); else console.log(response); }); // ... or retrying a specific number of times (10) -gcm.send(message, { registrationTokens: registrationTokens }, 10, function (err, response) { +gcm.send(message, registrationTokens, 10, function (err, response) { if(err) console.error(err); else console.log(response); }); @@ -98,17 +98,8 @@ gcm.send(message, { registrationTokens: registrationTokens }, 10, function (err, ## Recipients -You can send push notifications to various recipient types by providing one of the following recipient keys: - -|Key|Type|Description| -|---|---|---| -|to|String|A single [registration token](https://developers.google.com/cloud-messaging/android/client#sample-register), [notification key](https://developers.google.com/cloud-messaging/notifications), or [topic](https://developers.google.com/cloud-messaging/topic-messaging). -|topic|String|A single publish/subscribe topic. -|notificationKey|String|Deprecated. A key that groups multiple registration tokens linked to the same user. -|registrationIds|String[]|Deprecated. Use registrationTokens instead.| -|registrationTokens|String[]|A list of registration tokens. Must contain at least 1 and at most 1000 registration tokens.| - -If you provide an incorrect recipient key or object type, an `Error` object will be returned to your callback. +You can send a push notification to various recipient or topic, by providing a notification key, registration token og topic as a string. +Alternatively, you can send it to several recipients at once, by providing an array of registration tokens. Notice that [you can *at most* send notifications to 1000 registration tokens at a time](https://github.com/ToothlessGear/node-gcm/issues/42). This is due to [a restriction](http://developer.android.com/training/cloudsync/gcm.html) on the side of the GCM API. @@ -161,9 +152,9 @@ var gcm = require("node-gcm")('YOUR_API_KEY_HERE', requestOptions); // Prepare a GCM message... // Send it to GCM endpoint with modified request options -gcm.send(message, { registrationTokens: regTokens }, function (err, response) { +gcm.send(message, regTokens, function (err, response) { if(err) console.error(err); - else console.log(response); + else console.log(response); }); ``` From f3d0c9f26f70de8a86c418dbdd0ff959ac3e3353 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 1 Jun 2016 01:01:35 +0200 Subject: [PATCH 28/55] Fixed typo in README (og -> or) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6fb66d..b9aa1d1 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ gcm.send(message, registrationTokens, 10, function (err, response) { ## Recipients -You can send a push notification to various recipient or topic, by providing a notification key, registration token og topic as a string. +You can send a push notification to various recipient or topic, by providing a notification key, registration token or topic as a string. Alternatively, you can send it to several recipients at once, by providing an array of registration tokens. Notice that [you can *at most* send notifications to 1000 registration tokens at a time](https://github.com/ToothlessGear/node-gcm/issues/42). From 22a07f31fde4b254eefd33abe90c43abb8b73f06 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 1 Jun 2016 22:22:38 +0200 Subject: [PATCH 29/55] Remove sendNoRetry from being exposed --- lib/sender.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 1fd87ed..2f96252 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -24,12 +24,12 @@ Sender.prototype.send = function(message, recipient, options, callback) { options = cleanOptions(options); if(options.retries == 0) { - return this.sendNoRetry(message, recipient, callback); + return sendNoRetry(this.key, this.options, message, recipient, callback); } var self = this; - this.sendNoRetry(message, recipient, function(err, response, attemptedRegTokens) { + sendNoRetry(this.key, this.options, message, recipient, function(err, response, attemptedRegTokens) { if (err) { if (typeof err === 'number' && err > 399 && err < 500) { debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); @@ -125,7 +125,7 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { response.failure -= unsentRegTokens.length - retriedResponse.failure; } -Sender.prototype.sendNoRetry = function(message, recipient, callback) { +function sendNoRetry(key, options, message, recipient, callback) { if(!callback) { callback = function() {}; } @@ -139,11 +139,11 @@ Sender.prototype.sendNoRetry = function(message, recipient, callback) { var request_options = defaultsDeep({ method: 'POST', headers: { - 'Authorization': 'key=' + this.key + 'Authorization': 'key=' + key }, uri: Constants.GCM_SEND_URI, json: body - }, this.options, { + }, options, { timeout: Constants.SOCKET_TIMEOUT }); @@ -165,8 +165,8 @@ Sender.prototype.sendNoRetry = function(message, recipient, callback) { } callback(null, resBodyJSON, body.registration_ids || [ body.to ]); }); - }.bind(this)); -}; + }); +} function getRequestBody(message, recipient, callback) { var body = cleanParams(message); From 3d8b4f67787f71fe2d50ab4626e73b3b2b3b5bcc Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 1 Jun 2016 22:38:56 +0200 Subject: [PATCH 30/55] Rename sendNoRetry -> sendMessage --- lib/sender.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 2f96252..0a4c861 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -24,12 +24,12 @@ Sender.prototype.send = function(message, recipient, options, callback) { options = cleanOptions(options); if(options.retries == 0) { - return sendNoRetry(this.key, this.options, message, recipient, callback); + return sendMessage(this.key, this.options, message, recipient, callback); } var self = this; - sendNoRetry(this.key, this.options, message, recipient, function(err, response, attemptedRegTokens) { + sendMessage(this.key, this.options, message, recipient, function(err, response, attemptedRegTokens) { if (err) { if (typeof err === 'number' && err > 399 && err < 500) { debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); @@ -125,7 +125,7 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { response.failure -= unsentRegTokens.length - retriedResponse.failure; } -function sendNoRetry(key, options, message, recipient, callback) { +function sendMessage(key, options, message, recipient, callback) { if(!callback) { callback = function() {}; } From 81c27c3b62fe24393a6b00b530f391e0b8409e11 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 1 Jun 2016 22:39:41 +0200 Subject: [PATCH 31/55] Remove unnecessary support for no callback (always called with callback!) --- lib/sender.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 0a4c861..138fb92 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -126,10 +126,6 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { } function sendMessage(key, options, message, recipient, callback) { - if(!callback) { - callback = function() {}; - } - getRequestBody(message, recipient, function(err, body) { if(err) { return callback(err); From 3d187693b9ce3b717227cb3a96e6d2e53d82fe81 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 11:50:07 +0200 Subject: [PATCH 32/55] Rewrite sendNoRetry tests to use send with retries: 0 --- test/unit/senderSpec.js | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 90312a5..7f03054 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -38,7 +38,7 @@ describe('UNIT Sender', function () { }); }); - describe('sendNoRetry()', function () { + describe('send() without retries', function () { function setArgs(err, res, resBody) { args = { err: err, @@ -59,7 +59,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.proxy).to.equal(options.proxy); expect(args.options.maxSockets).to.equal(options.maxSockets); @@ -80,7 +80,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.method).to.not.equal(options.method); expect(args.options.headers).to.not.deep.equal(options.headers); @@ -98,7 +98,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.headers.Authorization).to.not.equal(options.headers.Auhtorization); done(); @@ -113,7 +113,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.headers.Custom).to.deep.equal(options.headers.Custom); done(); @@ -128,7 +128,7 @@ describe('UNIT Sender', function () { }; var sender = new Sender('mykey', options); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.strictSSL).to.be.an('undefined'); done(); @@ -138,7 +138,7 @@ describe('UNIT Sender', function () { it('should set the API key of req object if passed in API key', function (done) { var sender = new Sender('myKey'); var m = { data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.headers.Authorization).to.equal('key=myKey'); done(); @@ -148,7 +148,7 @@ describe('UNIT Sender', function () { it('should send a JSON object as the body of the request', function (done) { var sender = new Sender('mykey'); var m = { collapseKey: 'Message', data: {} }; - sender.sendNoRetry(m, '', function () {}); + sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { expect(args.options.json).to.be.a('object'); done(); @@ -166,7 +166,7 @@ describe('UNIT Sender', function () { } }; var sender = new Sender('mykey'); - sender.sendNoRetry(mess, '', function () {}); + sender.send(mess, '', { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.delay_while_idle).to.equal(mess.delay_while_idle); @@ -190,7 +190,7 @@ describe('UNIT Sender', function () { unknown_property: "hello" }; var sender = new Sender('mykey'); - sender.sendNoRetry(mess, '', function () {}); + sender.send(mess, '', { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.delay_while_idle).to.equal(undefined); @@ -206,7 +206,7 @@ describe('UNIT Sender', function () { it('should set the registration_ids to reg tokens implicitly passed in', function (done) { var sender = new Sender('myKey'); var m = { data: {} }; - sender.sendNoRetry(m, ["registration token 1", "registration token 2"], function () {}); + sender.send(m, ["registration token 1", "registration token 2"], { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.registration_ids).to.deep.equal(["registration token 1", "registration token 2"]); @@ -218,7 +218,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); var m = { data: {} }; var regTokens = ["registration token 1", "registration token 2"]; - sender.sendNoRetry(m, { registrationIds: regTokens }, function () {}); + sender.send(m, { registrationIds: regTokens }, { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.registration_ids).to.deep.equal(regTokens); @@ -230,7 +230,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); var m = { data: {} }; var regTokens = ["registration token 1", "registration token 2"]; - sender.sendNoRetry(m, { registrationTokens: regTokens }, function () {}); + sender.send(m, { registrationTokens: regTokens }, { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.registration_ids).to.deep.equal(regTokens); @@ -241,7 +241,7 @@ describe('UNIT Sender', function () { it('should set the to field if a single reg (or other) token is passed in', function(done) { var sender = new Sender('myKey'); var m = { data: {} }; - sender.sendNoRetry(m, "registration token 1", function () {}); + sender.send(m, "registration token 1", { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.to).to.deep.equal("registration token 1"); @@ -254,7 +254,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); var m = { data: {} }; var token = "registration token 1"; - sender.sendNoRetry(m, token, function () {}); + sender.send(m, token, { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.to).to.deep.equal(token); @@ -267,7 +267,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); var m = { data: {} }; var token = "registration token 1"; - sender.sendNoRetry(m, [ token ], function () {}); + sender.send(m, [ token ], { retries: 0 }, function () {}); setTimeout(function() { var body = args.options.json; expect(body.registration_ids).to.deep.equal([ token ]); @@ -279,7 +279,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if recipient is an empty object', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry({}, {}, callback); + sender.send({}, {}, { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -290,7 +290,7 @@ describe('UNIT Sender', function () { it('should pass an error into callback if no recipient provided', function (done) { var callback = sinon.spy(); var sender = new Sender('myKey'); - sender.sendNoRetry({}, [], callback); + sender.send({}, [], { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.be.a('object'); @@ -303,7 +303,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs('an error', {}, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.calledWith('an error')).to.be.ok; @@ -316,7 +316,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs(null, { statusCode: 500 }, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(500); @@ -329,7 +329,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs(null, { statusCode: 401 }, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(401); @@ -342,7 +342,7 @@ describe('UNIT Sender', function () { sender = new Sender('myKey'); setArgs(null, { statusCode: 400 }, {}); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(400); @@ -356,7 +356,7 @@ describe('UNIT Sender', function () { parseError = {error: 'Failed to parse JSON'}; setArgs(parseError, null, null); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.deep.equal(parseError); @@ -373,7 +373,7 @@ describe('UNIT Sender', function () { var sender = new Sender('myKey'); setArgs(null, { statusCode: 200 }, resBody); var m = { data: {} }; - sender.sendNoRetry(m, '', callback); + sender.send(m, '', { retries: 0 }, callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][1]).to.deep.equal(resBody); From 0e6d133c4016d05fda81b50b2efb3289e83085e0 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:00:46 +0200 Subject: [PATCH 33/55] Extract sendMessageWithRetries function --- lib/sender.js | 60 ++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 138fb92..c359c74 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -27,15 +27,42 @@ Sender.prototype.send = function(message, recipient, options, callback) { return sendMessage(this.key, this.options, message, recipient, callback); } - var self = this; + sendMessageWithRetries(this, this.key, this.options, message, recipient, options, callback); +}; + +function cleanOptions(options) { + if(!options || typeof options != "object") { + var retries = 5; + if(typeof options == "number") { + retries = options; + } + return { + retries: retries, + backoff: Constants.BACKOFF_INITIAL_DELAY + }; + } + + if(typeof options.retries != "number") { + options.retries = 5; + } + if(typeof options.backoff != "number") { + options.backoff = Constants.BACKOFF_INITIAL_DELAY; + } + if (options.backoff > Constants.MAX_BACKOFF_DELAY) { + options.backoff = Constants.MAX_BACKOFF_DELAY; + } + + return options; +} - sendMessage(this.key, this.options, message, recipient, function(err, response, attemptedRegTokens) { +function sendMessageWithRetries(self, key, senderOptions, message, recipient, messageOptions, callback) { + sendMessage(key, senderOptions, message, recipient, function(err, response, attemptedRegTokens) { if (err) { if (typeof err === 'number' && err > 399 && err < 500) { debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); return callback(err); } - return retry(self, message, recipient, options, callback); + return retry(self, message, recipient, messageOptions, callback); } if(!response.results) { return callback(null, response); @@ -50,7 +77,7 @@ Sender.prototype.send = function(message, recipient, options, callback) { debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); - retry(self, message, unsentRegTokens, options, function(err, retriedResponse) { + retry(self, message, unsentRegTokens, messageOptions, function(err, retriedResponse) { if(err) { return callback(null, response); } @@ -59,31 +86,6 @@ Sender.prototype.send = function(message, recipient, options, callback) { }); }); }); -}; - -function cleanOptions(options) { - if(!options || typeof options != "object") { - var retries = 5; - if(typeof options == "number") { - retries = options; - } - return { - retries: retries, - backoff: Constants.BACKOFF_INITIAL_DELAY - }; - } - - if(typeof options.retries != "number") { - options.retries = 5; - } - if(typeof options.backoff != "number") { - options.backoff = Constants.BACKOFF_INITIAL_DELAY; - } - if (options.backoff > Constants.MAX_BACKOFF_DELAY) { - options.backoff = Constants.MAX_BACKOFF_DELAY; - } - - return options; } function retry(self, message, recipient, options, callback) { From 008e2fda1e8ee2f3a48cfeebfba963612b4466b9 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:05:22 +0200 Subject: [PATCH 34/55] Modify retry to use sendMessageWithRetries --- lib/sender.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index c359c74..bd195f0 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -62,7 +62,7 @@ function sendMessageWithRetries(self, key, senderOptions, message, recipient, me debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); return callback(err); } - return retry(self, message, recipient, messageOptions, callback); + return retry(self, key, senderOptions, message, recipient, messageOptions, callback); } if(!response.results) { return callback(null, response); @@ -77,7 +77,7 @@ function sendMessageWithRetries(self, key, senderOptions, message, recipient, me debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); - retry(self, message, unsentRegTokens, messageOptions, function(err, retriedResponse) { + retry(self, key, senderOptions, message, unsentRegTokens, messageOptions, function(err, retriedResponse) { if(err) { return callback(null, response); } @@ -88,13 +88,13 @@ function sendMessageWithRetries(self, key, senderOptions, message, recipient, me }); } -function retry(self, message, recipient, options, callback) { +function retry(self, key, senderOptions, message, recipient, messageOptions, callback) { return setTimeout(function() { - self.send(message, recipient, { - retries: options.retries - 1, - backoff: options.backoff * 2 + sendMessageWithRetries(self, key, senderOptions, message, recipient, { + retries: messageOptions.retries - 1, + backoff: messageOptions.backoff * 2 }, callback); - }, options.backoff); + }, messageOptions.backoff); } function checkForBadTokens(results, originalRecipients, callback) { From cf7b494cb52d90bddbfbcabd24812f001e441774 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:05:43 +0200 Subject: [PATCH 35/55] Added a note about a case that seems odd --- lib/sender.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/sender.js b/lib/sender.js index bd195f0..0d88ad9 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -64,6 +64,7 @@ function sendMessageWithRetries(self, key, senderOptions, message, recipient, me } return retry(self, key, senderOptions, message, recipient, messageOptions, callback); } + //TODO: Figure out why this case exists -- should it be removed? if(!response.results) { return callback(null, response); } From 0236fbf0ec3835a39fd8a4bb3bacbde95ba3a176 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:07:24 +0200 Subject: [PATCH 36/55] Remove self argument from retry and sendMessageWithRetries --- lib/sender.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 0d88ad9..bff0af2 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -27,7 +27,7 @@ Sender.prototype.send = function(message, recipient, options, callback) { return sendMessage(this.key, this.options, message, recipient, callback); } - sendMessageWithRetries(this, this.key, this.options, message, recipient, options, callback); + sendMessageWithRetries(this.key, this.options, message, recipient, options, callback); }; function cleanOptions(options) { @@ -55,14 +55,14 @@ function cleanOptions(options) { return options; } -function sendMessageWithRetries(self, key, senderOptions, message, recipient, messageOptions, callback) { +function sendMessageWithRetries(key, senderOptions, message, recipient, messageOptions, callback) { sendMessage(key, senderOptions, message, recipient, function(err, response, attemptedRegTokens) { if (err) { if (typeof err === 'number' && err > 399 && err < 500) { debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); return callback(err); } - return retry(self, key, senderOptions, message, recipient, messageOptions, callback); + return retry(key, senderOptions, message, recipient, messageOptions, callback); } //TODO: Figure out why this case exists -- should it be removed? if(!response.results) { @@ -78,7 +78,7 @@ function sendMessageWithRetries(self, key, senderOptions, message, recipient, me debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); - retry(self, key, senderOptions, message, unsentRegTokens, messageOptions, function(err, retriedResponse) { + retry(key, senderOptions, message, unsentRegTokens, messageOptions, function(err, retriedResponse) { if(err) { return callback(null, response); } @@ -89,9 +89,9 @@ function sendMessageWithRetries(self, key, senderOptions, message, recipient, me }); } -function retry(self, key, senderOptions, message, recipient, messageOptions, callback) { +function retry(key, senderOptions, message, recipient, messageOptions, callback) { return setTimeout(function() { - sendMessageWithRetries(self, key, senderOptions, message, recipient, { + sendMessageWithRetries(key, senderOptions, message, recipient, { retries: messageOptions.retries - 1, backoff: messageOptions.backoff * 2 }, callback); From d125924554f695cae19c7d4b1ce20444c12668fc Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:14:56 +0200 Subject: [PATCH 37/55] Get request body early on, simply pass it through --- lib/sender.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index bff0af2..72d6f48 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -23,11 +23,16 @@ Sender.prototype.send = function(message, recipient, options, callback) { } options = cleanOptions(options); + getRequestBody(message, recipient, function(err, body) { + if(err) { + return callback(err); + } if(options.retries == 0) { - return sendMessage(this.key, this.options, message, recipient, callback); + return sendMessage(this.key, this.options, body, callback); } - sendMessageWithRetries(this.key, this.options, message, recipient, options, callback); + sendMessageWithRetries(this.key, this.options, body, options, callback); + }.bind(this)); }; function cleanOptions(options) { @@ -55,14 +60,14 @@ function cleanOptions(options) { return options; } -function sendMessageWithRetries(key, senderOptions, message, recipient, messageOptions, callback) { - sendMessage(key, senderOptions, message, recipient, function(err, response, attemptedRegTokens) { +function sendMessageWithRetries(key, senderOptions, body, messageOptions, callback) { + sendMessage(key, senderOptions, body, function(err, response, attemptedRegTokens) { if (err) { if (typeof err === 'number' && err > 399 && err < 500) { debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); return callback(err); } - return retry(key, senderOptions, message, recipient, messageOptions, callback); + return retry(key, senderOptions, body, messageOptions, callback); } //TODO: Figure out why this case exists -- should it be removed? if(!response.results) { @@ -78,7 +83,8 @@ function sendMessageWithRetries(key, senderOptions, message, recipient, messageO debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); - retry(key, senderOptions, message, unsentRegTokens, messageOptions, function(err, retriedResponse) { + body.registration_ids = unsentRegTokens; + retry(key, senderOptions, body, messageOptions, function(err, retriedResponse) { if(err) { return callback(null, response); } @@ -89,9 +95,9 @@ function sendMessageWithRetries(key, senderOptions, message, recipient, messageO }); } -function retry(key, senderOptions, message, recipient, messageOptions, callback) { +function retry(key, senderOptions, body, messageOptions, callback) { return setTimeout(function() { - sendMessageWithRetries(key, senderOptions, message, recipient, { + sendMessageWithRetries(key, senderOptions, body, { retries: messageOptions.retries - 1, backoff: messageOptions.backoff * 2 }, callback); @@ -128,12 +134,7 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { response.failure -= unsentRegTokens.length - retriedResponse.failure; } -function sendMessage(key, options, message, recipient, callback) { - getRequestBody(message, recipient, function(err, body) { - if(err) { - return callback(err); - } - +function sendMessage(key, options, body, callback) { //Build request options, allowing some to be overridden var request_options = defaultsDeep({ method: 'POST', @@ -164,7 +165,6 @@ function sendMessage(key, options, message, recipient, callback) { } callback(null, resBodyJSON, body.registration_ids || [ body.to ]); }); - }); } function getRequestBody(message, recipient, callback) { From d4eec160882a219f17ee9962699767e944ba43d5 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:15:46 +0200 Subject: [PATCH 38/55] fix indentation --- lib/sender.js | 67 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 72d6f48..d1f21cf 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -27,11 +27,10 @@ Sender.prototype.send = function(message, recipient, options, callback) { if(err) { return callback(err); } - if(options.retries == 0) { - return sendMessage(this.key, this.options, body, callback); - } - - sendMessageWithRetries(this.key, this.options, body, options, callback); + if(options.retries == 0) { + return sendMessage(this.key, this.options, body, callback); + } + sendMessageWithRetries(this.key, this.options, body, options, callback); }.bind(this)); }; @@ -135,36 +134,36 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { } function sendMessage(key, options, body, callback) { - //Build request options, allowing some to be overridden - var request_options = defaultsDeep({ - method: 'POST', - headers: { - 'Authorization': 'key=' + key - }, - uri: Constants.GCM_SEND_URI, - json: body - }, options, { - timeout: Constants.SOCKET_TIMEOUT - }); + //Build request options, allowing some to be overridden + var request_options = defaultsDeep({ + method: 'POST', + headers: { + 'Authorization': 'key=' + key + }, + uri: Constants.GCM_SEND_URI, + json: body + }, options, { + timeout: Constants.SOCKET_TIMEOUT + }); - request(request_options, function (err, res, resBodyJSON) { - if (err) { - return callback(err); - } - if (res.statusCode >= 500) { - debug('GCM service is unavailable (500)'); - return callback(res.statusCode); - } - if (res.statusCode === 401) { - debug('Unauthorized (401). Check that your API token is correct.'); - return callback(res.statusCode); - } - if (res.statusCode !== 200) { - debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON); - return callback(res.statusCode); - } - callback(null, resBodyJSON, body.registration_ids || [ body.to ]); - }); + request(request_options, function (err, res, resBodyJSON) { + if (err) { + return callback(err); + } + if (res.statusCode >= 500) { + debug('GCM service is unavailable (500)'); + return callback(res.statusCode); + } + if (res.statusCode === 401) { + debug('Unauthorized (401). Check that your API token is correct.'); + return callback(res.statusCode); + } + if (res.statusCode !== 200) { + debug('Invalid request (' + res.statusCode + '): ' + resBodyJSON); + return callback(res.statusCode); + } + callback(null, resBodyJSON, body.registration_ids || [ body.to ]); + }); } function getRequestBody(message, recipient, callback) { From d174ce752ecaeaed849207dd3eb6a03dc0a09997 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:29:23 +0200 Subject: [PATCH 39/55] Build requestOptions early and pass around (instead of building for each request! --- lib/sender.js | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index d1f21cf..387e459 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -9,8 +9,15 @@ function Sender(key, options) { return new Sender(key, options); } - this.key = key; - this.options = options || {}; + this.requestOptions = defaultsDeep({ + method: 'POST', + headers: { + 'Authorization': 'key=' + key + }, + uri: Constants.GCM_SEND_URI + }, options, { + timeout: Constants.SOCKET_TIMEOUT + }); } Sender.prototype.send = function(message, recipient, options, callback) { @@ -28,9 +35,9 @@ Sender.prototype.send = function(message, recipient, options, callback) { return callback(err); } if(options.retries == 0) { - return sendMessage(this.key, this.options, body, callback); + return sendMessage(this.requestOptions, body, callback); } - sendMessageWithRetries(this.key, this.options, body, options, callback); + sendMessageWithRetries(this.requestOptions, body, options, callback); }.bind(this)); }; @@ -59,14 +66,14 @@ function cleanOptions(options) { return options; } -function sendMessageWithRetries(key, senderOptions, body, messageOptions, callback) { - sendMessage(key, senderOptions, body, function(err, response, attemptedRegTokens) { +function sendMessageWithRetries(requestOptions, body, messageOptions, callback) { + sendMessage(requestOptions, body, function(err, response, attemptedRegTokens) { if (err) { if (typeof err === 'number' && err > 399 && err < 500) { debug("Error 4xx -- no use retrying. Something is wrong with the request (probably authentication?)"); return callback(err); } - return retry(key, senderOptions, body, messageOptions, callback); + return retry(requestOptions, body, messageOptions, callback); } //TODO: Figure out why this case exists -- should it be removed? if(!response.results) { @@ -83,7 +90,7 @@ function sendMessageWithRetries(key, senderOptions, body, messageOptions, callba debug("Retrying " + unsentRegTokens.length + " unsent registration tokens"); body.registration_ids = unsentRegTokens; - retry(key, senderOptions, body, messageOptions, function(err, retriedResponse) { + retry(requestOptions, body, messageOptions, function(err, retriedResponse) { if(err) { return callback(null, response); } @@ -94,9 +101,9 @@ function sendMessageWithRetries(key, senderOptions, body, messageOptions, callba }); } -function retry(key, senderOptions, body, messageOptions, callback) { +function retry(requestOptions, body, messageOptions, callback) { return setTimeout(function() { - sendMessageWithRetries(key, senderOptions, body, { + sendMessageWithRetries(requestOptions, body, { retries: messageOptions.retries - 1, backoff: messageOptions.backoff * 2 }, callback); @@ -133,20 +140,9 @@ function updateResponseMetaData(response, retriedResponse, unsentRegTokens) { response.failure -= unsentRegTokens.length - retriedResponse.failure; } -function sendMessage(key, options, body, callback) { - //Build request options, allowing some to be overridden - var request_options = defaultsDeep({ - method: 'POST', - headers: { - 'Authorization': 'key=' + key - }, - uri: Constants.GCM_SEND_URI, - json: body - }, options, { - timeout: Constants.SOCKET_TIMEOUT - }); - - request(request_options, function (err, res, resBodyJSON) { +function sendMessage(requestOptions, body, callback) { + requestOptions.json = body; + request(requestOptions, function (err, res, resBodyJSON) { if (err) { return callback(err); } From e177346a8c2eeadf143de4c967066f1d66b02be7 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 12:30:42 +0200 Subject: [PATCH 40/55] Reorder functions --- lib/sender.js | 78 +++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 387e459..17f6445 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -66,6 +66,45 @@ function cleanOptions(options) { return options; } +function getRequestBody(message, recipient, callback) { + var body = cleanParams(message); + + if(typeof recipient == "string") { + body.to = recipient; + return nextTick(callback, null, body); + } + if(Array.isArray(recipient)) { + if(recipient.length < 1) { + return nextTick(callback, new Error('Empty recipient array passed!')); + } + body.registration_ids = recipient; + return nextTick(callback, null, body); + } + return nextTick(callback, new Error('Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!')); +} + +function cleanParams(raw) { + var params = {}; + Object.keys(raw).forEach(function(param) { + var paramOptions = messageOptions[param]; + if(!paramOptions) { + return console.warn("node-gcm ignored unknown message parameter " + param); + } + if(paramOptions.__argType != typeof raw[param]) { + return console.warn("node-gcm ignored wrongly typed message parameter " + param + " (was " + typeof raw[param] + ", expected " + paramOptions.__argType + ")"); + } + params[param] = raw[param]; + }); + return params; +} + +function nextTick(func) { + var args = Array.prototype.slice.call(arguments, 1); + process.nextTick(function() { + func.apply(this, args); + }.bind(this)); +} + function sendMessageWithRetries(requestOptions, body, messageOptions, callback) { sendMessage(requestOptions, body, function(err, response, attemptedRegTokens) { if (err) { @@ -162,43 +201,4 @@ function sendMessage(requestOptions, body, callback) { }); } -function getRequestBody(message, recipient, callback) { - var body = cleanParams(message); - - if(typeof recipient == "string") { - body.to = recipient; - return nextTick(callback, null, body); - } - if(Array.isArray(recipient)) { - if(recipient.length < 1) { - return nextTick(callback, new Error('Empty recipient array passed!')); - } - body.registration_ids = recipient; - return nextTick(callback, null, body); - } - return nextTick(callback, new Error('Invalid recipient (' + recipient + ', type ' + typeof recipient + ') provided (must be array or string)!')); -} - -function cleanParams(raw) { - var params = {}; - Object.keys(raw).forEach(function(param) { - var paramOptions = messageOptions[param]; - if(!paramOptions) { - return console.warn("node-gcm ignored unknown message parameter " + param); - } - if(paramOptions.__argType != typeof raw[param]) { - return console.warn("node-gcm ignored wrongly typed message parameter " + param + " (was " + typeof raw[param] + ", expected " + paramOptions.__argType + ")"); - } - params[param] = raw[param]; - }); - return params; -} - -function nextTick(func) { - var args = Array.prototype.slice.call(arguments, 1); - process.nextTick(function() { - func.apply(this, args); - }.bind(this)); -} - module.exports = Sender; From c558a7cf093f818085273121c8799d098e31f4b6 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 23:16:53 +0200 Subject: [PATCH 41/55] Remove an irrelevant return --- lib/sender.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sender.js b/lib/sender.js index 17f6445..df24c74 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -141,7 +141,7 @@ function sendMessageWithRetries(requestOptions, body, messageOptions, callback) } function retry(requestOptions, body, messageOptions, callback) { - return setTimeout(function() { + setTimeout(function() { sendMessageWithRetries(requestOptions, body, { retries: messageOptions.retries - 1, backoff: messageOptions.backoff * 2 From 7a0e08e9f491b2eb8c25465fb61b4165d1a31720 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 23:24:33 +0200 Subject: [PATCH 42/55] Removed redundant tests (now covered by send with retries: 0) --- test/unit/senderSpec.js | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 7f03054..8b033e9 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -422,36 +422,6 @@ describe('UNIT Sender', function () { Sender.prototype.sendNoRetry = restore.sendNoRetry; }); - it('should pass reg tokens to sendNoRetry, even if it is an empty array', function (done) { - var emptyRegTokenArray = []; - var callback = function(error) { - expect(args.reg_tokens).to.equal(emptyRegTokenArray); - done(); - }; - var sender = new Sender('myKey'); - sender.send({}, emptyRegTokenArray, 0, callback); - }); - - it('should pass reg tokens to sendNoRetry, even if it is an empty object', function (done) { - var emptyRegTokenObject = {}; - var callback = function(error) { - expect(args.reg_tokens).to.equal(emptyRegTokenObject); - done(); - }; - var sender = new Sender('myKey'); - sender.send({}, emptyRegTokenObject, 0, callback); - }); - - it('should pass reg tokens to sendNoRetry, even if some keys are invalid', function (done) { - var invalidRegTokenObject = { invalid: ['regToken'] }; - var callback = function(error) { - expect(args.reg_tokens).to.equal(invalidRegTokenObject); - done(); - }; - var sender = new Sender('myKey'); - sender.send({}, invalidRegTokenObject, 0, callback); - }); - it('should pass the message and the regToken to sendNoRetry on call', function () { var sender = new Sender('myKey'), message = { data: {} }, From e0a6969d013af110b4452e3ac791c2491902f5bd Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 23:27:12 +0200 Subject: [PATCH 43/55] Dont try to mock sendNoRetry --- test/unit/senderSpec.js | 41 +++++++---------------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 8b033e9..ce7667c 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -45,7 +45,7 @@ describe('UNIT Sender', function () { res: res, resBody: resBody }; - }; + } before(function() { setArgs(null, { statusCode: 200 }, {}); }); @@ -383,43 +383,16 @@ describe('UNIT Sender', function () { }); describe('send()', function () { - var restore = {}; - // Set args passed into sendNoRetry - function setArgs(err, response) { + function setArgs(err, res, resBody) { args = { err: err, - response: response, - tries: 0 - }; - }; - - before( function () { - restore.sendNoRetry = Sender.prototype.sendNoRetry; - Sender.prototype.sendNoRetry = function (message, reg_tokens, callback) { - args.message = message; - args.reg_tokens = reg_tokens; - args.tries++; - var nextResponse; - if(!args.response) { - nextResponse = args.response; - } - else if(args.response.length > 1) { - nextResponse = args.response.slice(0,1)[0]; - args.response = args.response.slice(1,args.response.length); - } - else if(args.response.length == 1) { - args.response = args.response[0]; - nextResponse = args.response; - } - else { - nextResponse = args.response; - } - callback( args.err, nextResponse, args.reg_tokens ); + res: res, + resBody: resBody }; - }); + } - after( function () { - Sender.prototype.sendNoRetry = restore.sendNoRetry; + before(function() { + setArgs(null, { statusCode: 200 }, {}); }); it('should pass the message and the regToken to sendNoRetry on call', function () { From e8dd0ffbccf01cc1c0feee6338cba0e036f0099f Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 23:27:51 +0200 Subject: [PATCH 44/55] Removed some more redundant tests --- test/unit/senderSpec.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index ce7667c..51c94ad 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -395,28 +395,6 @@ describe('UNIT Sender', function () { setArgs(null, { statusCode: 200 }, {}); }); - it('should pass the message and the regToken to sendNoRetry on call', function () { - var sender = new Sender('myKey'), - message = { data: {} }, - regToken = [24]; - setArgs(null, {}); - sender.send(message, regToken, 0, function () {}); - expect(args.message).to.equal(message); - expect(args.reg_tokens).to.equal(regToken); - expect(args.tries).to.equal(1); - }); - - it('should pass the message and the regTokens to sendNoRetry on call', function () { - var sender = new Sender('myKey'), - message = { data: {} }, - regTokens = [24, 34, 44]; - setArgs(null, {}); - sender.send(message, regTokens, 0, function () {}); - expect(args.message).to.equal(message); - expect(args.reg_tokens).to.equal(regTokens); - expect(args.tries).to.equal(1); - }); - it('should pass the response into callback if successful for token', function () { var callback = sinon.spy(), response = { success: true }, From 9c32fb2cfdcddc4aef7c9f9d780dfe2ce298b99d Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 2 Jun 2016 23:30:29 +0200 Subject: [PATCH 45/55] Make all send specs async --- test/unit/senderSpec.js | 44 ++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 51c94ad..6546254 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -395,48 +395,60 @@ describe('UNIT Sender', function () { setArgs(null, { statusCode: 200 }, {}); }); - it('should pass the response into callback if successful for token', function () { + it('should pass the response into callback if successful for token', function (done) { var callback = sinon.spy(), response = { success: true }, sender = new Sender('myKey'); setArgs(null, response); sender.send({}, [1], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][1]).to.equal(response); - expect(args.tries).to.equal(1); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][1]).to.equal(response); + expect(args.tries).to.equal(1); + done(); + }, 10); }); - it('should pass the response into callback if successful for tokens', function () { + it('should pass the response into callback if successful for tokens', function (done) { var callback = sinon.spy(), response = { success: true }, sender = new Sender('myKey'); setArgs(null, response); sender.send({}, [1, 2, 3], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][1]).to.equal(response); - expect(args.tries).to.equal(1); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][1]).to.equal(response); + expect(args.tries).to.equal(1); + done(); + }, 10); }); - it('should pass the error into callback if failure and no retry for token', function () { + it('should pass the error into callback if failure and no retry for token', function (done) { var callback = sinon.spy(), error = 'my error', sender = new Sender('myKey'); setArgs(error); sender.send({}, [1], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.equal(error); - expect(args.tries).to.equal(1); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][0]).to.equal(error); + expect(args.tries).to.equal(1); + done(); + }, 10); }); - it('should pass the error into callback if failure and no retry for tokens', function () { + it('should pass the error into callback if failure and no retry for tokens', function (done) { var callback = sinon.spy(), error = 'my error', sender = new Sender('myKey'); setArgs(error); sender.send({}, [1, 2, 3], 0, callback); - expect(callback.calledOnce).to.be.ok; - expect(callback.args[0][0]).to.equal(error); - expect(args.tries).to.equal(1); + setTimeout(function() { + expect(callback.calledOnce).to.be.ok; + expect(callback.args[0][0]).to.equal(error); + expect(args.tries).to.equal(1); + done(); + }, 10); }); it('should retry number of times passed into call and do exponential backoff', function (done) { From 79873f8ee848329d27a3e553558dbb880e9c614c Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 8 Jun 2016 19:37:10 +0200 Subject: [PATCH 46/55] Break out of retry if there are no retries left! --- lib/sender.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/sender.js b/lib/sender.js index df24c74..ebce102 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -142,6 +142,9 @@ function sendMessageWithRetries(requestOptions, body, messageOptions, callback) function retry(requestOptions, body, messageOptions, callback) { setTimeout(function() { + if(messageOptions.retries <= 1) { + return sendMessage(requestOptions, body, callback); + } sendMessageWithRetries(requestOptions, body, { retries: messageOptions.retries - 1, backoff: messageOptions.backoff * 2 From 20fad62ada352e8cdc787f88cab1621d7e323deb Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 8 Jun 2016 19:37:40 +0200 Subject: [PATCH 47/55] Remove this one weird case that is good for naught --- lib/sender.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index ebce102..7a4c24f 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -114,10 +114,6 @@ function sendMessageWithRetries(requestOptions, body, messageOptions, callback) } return retry(requestOptions, body, messageOptions, callback); } - //TODO: Figure out why this case exists -- should it be removed? - if(!response.results) { - return callback(null, response); - } checkForBadTokens(response.results, attemptedRegTokens, function(err, unsentRegTokens, regTokenPositionMap) { if(err) { return callback(err); From 41b2d94a9631fff9a5742895b4c5ebdc89240560 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 8 Jun 2016 19:40:22 +0200 Subject: [PATCH 48/55] Make send() tests work. This required some major changes to how the tests are structured, as I didnt want to be listening on what used to be "sendNoRetry". requestStub is now a sinon spy. it is reset before EVERY test (woops, before we only cleared state before each test CATEGORY) and how we expect some things has changed as a result of these structural changes --- test/unit/senderSpec.js | 85 ++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 6546254..d5bcbc0 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -9,10 +9,15 @@ var chai = require('chai'), describe('UNIT Sender', function () { // Use object to set arguments passed into callback var args = {}; - var requestStub = function (options, callback) { + var requestStub = sinon.spy(function (options, callback) { args.options = options; - return callback( args.err, args.res, args.resBody ); - }; + var resBody = args.resBody; + if(Array.isArray(args.resBody)) { + resBody = args.resBody[0]; + args.resBody = args.resBody.slice(1); + } + return callback( args.err, args.res, resBody ); + }); var Sender = proxyquire(senderPath, { 'request': requestStub }); describe('constructor', function () { @@ -46,7 +51,8 @@ describe('UNIT Sender', function () { resBody: resBody }; } - before(function() { + beforeEach(function() { + requestStub.reset(); setArgs(null, { statusCode: 200 }, {}); }); @@ -391,39 +397,52 @@ describe('UNIT Sender', function () { }; } - before(function() { + beforeEach(function() { + requestStub.reset(); setArgs(null, { statusCode: 200 }, {}); }); it('should pass the response into callback if successful for token', function (done) { var callback = sinon.spy(), - response = { success: true }, + response = { + success: 1, + failure: 0, + results: [ + { message_id: "something" } + ] + }, sender = new Sender('myKey'); - setArgs(null, response); - sender.send({}, [1], 0, callback); + setArgs(null, { statusCode: 200 }, response); + sender.send({}, [1], callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][1]).to.equal(response); - expect(args.tries).to.equal(1); + expect(requestStub.args.length).to.equal(1); done(); }, 10); }); it('should pass the response into callback if successful for tokens', function (done) { var callback = sinon.spy(), - response = { success: true }, + response = { + success: 1, + failure: 0, + results: [ + { message_id: "something" } + ] + }, sender = new Sender('myKey'); - setArgs(null, response); - sender.send({}, [1, 2, 3], 0, callback); + setArgs(null, { statusCode: 200 }, response); + sender.send({}, [1, 2, 3], callback); setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][1]).to.equal(response); - expect(args.tries).to.equal(1); + expect(requestStub.args.length).to.equal(1); done(); }, 10); }); - it('should pass the error into callback if failure and no retry for token', function (done) { + it('should pass the error into callback if failure for token', function (done) { var callback = sinon.spy(), error = 'my error', sender = new Sender('myKey'); @@ -432,12 +451,12 @@ describe('UNIT Sender', function () { setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(error); - expect(args.tries).to.equal(1); + expect(requestStub.args.length).to.equal(1); done(); }, 10); }); - it('should pass the error into callback if failure and no retry for tokens', function (done) { + it('should pass the error into callback if failure for tokens', function (done) { var callback = sinon.spy(), error = 'my error', sender = new Sender('myKey'); @@ -446,7 +465,7 @@ describe('UNIT Sender', function () { setTimeout(function() { expect(callback.calledOnce).to.be.ok; expect(callback.args[0][0]).to.equal(error); - expect(args.tries).to.equal(1); + expect(requestStub.args.length).to.equal(1); done(); }, 10); }); @@ -454,25 +473,28 @@ describe('UNIT Sender', function () { it('should retry number of times passed into call and do exponential backoff', function (done) { var start = new Date(); var callback = function () { - expect(args.tries).to.equal(2); - expect(new Date() - start).to.be.gte(1000); + expect(requestStub.args.length).to.equal(2); + expect(new Date() - start).to.be.gte(200); done(); }; var sender = new Sender('myKey'); - setArgs('my error'); - sender.send({ data: {}}, [1], 1, callback); + setArgs(null, { statusCode: 500 }); + sender.send({ data: {}}, [1], { + retries: 1, + backoff: 200 + }, callback); }); it('should retry if not all regTokens were successfully sent', function (done) { var callback = function () { - expect(args.tries).to.equal(3); - // Last call of sendNoRetry should be for only failed regTokens - expect(args.reg_tokens.length).to.equal(1); - expect(args.reg_tokens[0]).to.equal(3); + expect(requestStub.args.length).to.equal(3); + var requestOptions = requestStub.args[2][0]; + expect(requestOptions.json.registration_ids.length).to.equal(1); + expect(requestOptions.json.registration_ids[0]).to.equal(3); done(); }; var sender = new Sender('myKey'); - setArgs(null, [{ results: [{}, { error: 'Unavailable' }, { error: 'Unavailable' }]}, { results: [ {}, { error: 'Unavailable' } ] }, { results: [ {} ] } ]); + setArgs(null, { statusCode: 200}, [{ results: [{}, { error: 'Unavailable' }, { error: 'Unavailable' }]}, { results: [ {}, { error: 'Unavailable' } ] }, { results: [ {} ] } ]); sender.send({ data: {}}, [1,2,3], { retries: 5, backoff: 100 @@ -482,8 +504,9 @@ describe('UNIT Sender', function () { it('should retry all regTokens in event of an error', function (done) { var start = new Date(); var callback = function () { - expect(args.tries).to.equal(2); - expect(args.reg_tokens.length).to.equal(3); + expect(requestStub.args.length).to.equal(2); + var requestOptions = requestStub.args[1][0]; + expect(requestOptions.json.registration_ids.length).to.equal(3); done(); }; var sender = new Sender('myKey'); @@ -500,7 +523,7 @@ describe('UNIT Sender', function () { done(); }; var sender = new Sender('myKey'); - setArgs(null, [ + setArgs(null, { statusCode: 200 }, [ { success: 1, failure: 2, canonical_ids: 0, results: [ {}, { error: 'Unavailable' }, { error: 'Unavailable' } ] }, { success: 1, canonical_ids: 1, failure: 0, results: [ {}, {} ] } ]); @@ -516,13 +539,13 @@ describe('UNIT Sender', function () { done(); }; var sender = new Sender('myKey'); - setArgs(null, [ + setArgs(null, { statusCode: 200 }, [ { success: 0, failure: 3, canonical_ids: 0, results: [ { error: 'Unavailable' }, { error: 'Unavailable' }, { error: 'Unavailable' } ] }, { success: 1, canonical_ids: 0, failure: 2, results: [ { error: 'Unavailable' }, { error: 'Unavailable' }, {} ] }, { success: 0, canonical_ids: 0, failure: 2, results: [ { error: 'Unavailable' }, { error: 'Unavailable' } ] } ]); sender.send({ data: {}}, [1,2,3], { - retries: 3, + retries: 2, backoff: 100 }, callback); }); From a1343eb1410d75199f415e7f4e04304be9ebec0a Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 8 Jun 2016 19:43:13 +0200 Subject: [PATCH 49/55] merge two tests, so the same on now tests sender options and auth key --- test/unit/senderSpec.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index d5bcbc0..a15ef2b 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -28,19 +28,6 @@ describe('UNIT Sender', function () { expect(sender).to.not.be.undefined; expect(sender).to.be.instanceOf(gcm); }); - - it('should create a Sender with key and options passed in', function () { - var options = { - proxy: 'http://myproxy.com', - maxSockets: 100, - timeout: 100 - }; - var key = 'myAPIKey', - sender = new gcm(key, options); - expect(sender).to.be.instanceOf(gcm); - expect(sender.key).to.equal(key); - expect(sender.options).to.deep.equal(options); - }); }); describe('send() without retries', function () { @@ -56,7 +43,7 @@ describe('UNIT Sender', function () { setArgs(null, { statusCode: 200 }, {}); }); - it('should set proxy, maxSockets, timeout and/or strictSSL of req object if passed into constructor', function (done) { + it('should set key, proxy, maxSockets, timeout and/or strictSSL of req object if passed into constructor', function (done) { var options = { proxy: 'http://myproxy.com', maxSockets: 100, @@ -67,6 +54,7 @@ describe('UNIT Sender', function () { var m = { data: {} }; sender.send(m, '', { retries: 0 }, function () {}); setTimeout(function() { + expect(args.options.headers["Authorization"]).to.equal("key=mykey"); expect(args.options.proxy).to.equal(options.proxy); expect(args.options.maxSockets).to.equal(options.maxSockets); expect(args.options.timeout).to.equal(options.timeout); From 7e556990ac9be287819be290fef57264dc7078e9 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Wed, 8 Jun 2016 19:45:05 +0200 Subject: [PATCH 50/55] Remove two tests that no longer pass (and shouldnt!) They used the registration_ids key, which we will no longer support. They passed because state was NOT reset before every test (fixed this in earlier commit) --- test/unit/senderSpec.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index a15ef2b..0670b20 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -208,30 +208,6 @@ describe('UNIT Sender', function () { }, 10); }); - it('should set the registration_ids to reg tokens explicitly passed in', function (done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var regTokens = ["registration token 1", "registration token 2"]; - sender.send(m, { registrationIds: regTokens }, { retries: 0 }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.registration_ids).to.deep.equal(regTokens); - done(); - }, 10); - }); - - it('should set the registration_ids to reg tokens explicitly passed in', function (done) { - var sender = new Sender('myKey'); - var m = { data: {} }; - var regTokens = ["registration token 1", "registration token 2"]; - sender.send(m, { registrationTokens: regTokens }, { retries: 0 }, function () {}); - setTimeout(function() { - var body = args.options.json; - expect(body.registration_ids).to.deep.equal(regTokens); - done(); - }, 10); - }); - it('should set the to field if a single reg (or other) token is passed in', function(done) { var sender = new Sender('myKey'); var m = { data: {} }; From c89d01ac636cafcb279ce86fd59585671f318ea6 Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 9 Jun 2016 18:12:07 +0200 Subject: [PATCH 51/55] Update README for no sendNoRetry --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9aa1d1..d92761c 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ registrationTokens.push('regToken2'); // Send the message // ... trying only once -gcm.sendNoRetry(message, registrationTokens, function(err, response) { +gcm.send(message, registrationTokens, { retries: 0 }, function(err, response) { if(err) console.error(err); else console.log(response); }); From 3476392622afce6e434423bc2739daa6711e135a Mon Sep 17 00:00:00 2001 From: Niels Abildgaard Date: Thu, 9 Jun 2016 18:12:27 +0200 Subject: [PATCH 52/55] vbump 1.0.0-alpha.1 and CHANGELOG --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f52ff..2f5b926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog ========= +**1.0.0-alpha.1** + * Removed the Message abstraction, now expect plain objects. + * Smaller lodash dependency (only depending on the part that is used). + * Simplified the recipient argument so it is now closer to the actual API interface. + * Removed `sendNoRetry` method on sender --- use `send` with the option `retries: 0` instead. + **1.0.0-alpha.0** * Removed deprecated things: constants, Result, MulticastResult, Message#addDataWith... diff --git a/package.json b/package.json index 303e167..c86199c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "node-gcm", "description": "Easy interface for Google's Cloud Messaging service (now Firebase Cloud Messaging)", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.1", "author": "Marcus Farkas ", "contributors": [ "Marcus Farkas ", From 5fd55d7f33c260a2062a5cb4b8f738f667282d45 Mon Sep 17 00:00:00 2001 From: Ratson Date: Sun, 17 Jul 2016 23:06:05 +0800 Subject: [PATCH 53/55] Upgrade debug --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c86199c..b7ca8e7 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "test": "mocha test/**/*Spec.js" }, "dependencies": { - "debug": "^0.8.1", + "debug": "^2.2.0", "lodash.defaultsdeep": "^4.4.0", "request": "^2.27.0" }, From 7876a2dc102fc5a1655e21d379c3b1df4190c0ed Mon Sep 17 00:00:00 2001 From: Ratson Date: Thu, 4 Aug 2016 00:07:54 +0800 Subject: [PATCH 54/55] Update contributors --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b7ca8e7..8deef44 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "Elad Nava (https://github.com/eladnava)", "Marc Obrador (https://github.com/marcobrador)", "Chirag Choudhary (https://github.com/chirag200666)", - "Alexander Amin (https://github.com/AlexAmin)" + "Alexander Amin (https://github.com/AlexAmin)", + "Ratson (https://github.com/ratson)" ], "repository": { "type": "git", From fb8cbb8dabebce6bdb22adabdce528019094a5f5 Mon Sep 17 00:00:00 2001 From: Dan Perron Date: Wed, 20 Jul 2016 14:36:14 -0500 Subject: [PATCH 55/55] Return a Promise from Sender.send() if it is called without a callback. --- lib/sender.js | 11 ++- package.json | 3 +- test/unit/senderSpec.js | 145 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 2 deletions(-) diff --git a/lib/sender.js b/lib/sender.js index 7a4c24f..e90d015 100644 --- a/lib/sender.js +++ b/lib/sender.js @@ -21,12 +21,20 @@ function Sender(key, options) { } Sender.prototype.send = function(message, recipient, options, callback) { + var rVal; if(typeof options == "function") { callback = options; options = null; } else if(!callback) { - callback = function() {}; + rVal = new Promise(function(resolve, reject) { + callback = function(err, response) { + if (err) { + return reject(err); + } + return resolve(response); + } + }); } options = cleanOptions(options); @@ -39,6 +47,7 @@ Sender.prototype.send = function(message, recipient, options, callback) { } sendMessageWithRetries(this.requestOptions, body, options, callback); }.bind(this)); + return rVal; }; function cleanOptions(options) { diff --git a/package.json b/package.json index 8deef44..921a02d 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "Marc Obrador (https://github.com/marcobrador)", "Chirag Choudhary (https://github.com/chirag200666)", "Alexander Amin (https://github.com/AlexAmin)", - "Ratson (https://github.com/ratson)" + "Ratson (https://github.com/ratson)", + "Dan Perron (https://github.com/dan-perron)" ], "repository": { "type": "git", diff --git a/test/unit/senderSpec.js b/test/unit/senderSpec.js index 0670b20..39aed07 100644 --- a/test/unit/senderSpec.js +++ b/test/unit/senderSpec.js @@ -451,6 +451,8 @@ describe('UNIT Sender', function () { it('should retry if not all regTokens were successfully sent', function (done) { var callback = function () { + // These expect calls don't get reported to the test runner, they just result + // in a timeout. expect(requestStub.args.length).to.equal(3); var requestOptions = requestStub.args[2][0]; expect(requestOptions.json.registration_ids.length).to.equal(1); @@ -514,4 +516,147 @@ describe('UNIT Sender', function () { }, callback); }); }); + + describe('send() with promise', function () { + function setArgs(err, res, resBody) { + args = { + err: err, + res: res, + resBody: resBody + }; + } + + beforeEach(function() { + requestStub.reset(); + setArgs(null, { statusCode: 200 }, {}); + }); + + it('should return the response in the promise if successful for token', function () { + var response = { + success: 1, + failure: 0, + results: [ + { message_id: "something" } + ] + }, + sender = new Sender('myKey'); + setArgs(null, { statusCode: 200 }, response); + return sender.send({}, [1]).then(function(output) { + expect(output).to.equal(response); + expect(requestStub.args.length).to.equal(1); + }); + }); + + it('should return the response in the promise if successful for tokens', function () { + var response = { + success: 1, + failure: 0, + results: [ + { message_id: "something" } + ] + }, + sender = new Sender('myKey'); + setArgs(null, { statusCode: 200 }, response); + return sender.send({}, [1, 2, 3]).then(function(output) { + expect(output).to.equal(response); + expect(requestStub.args.length).to.equal(1); + }); + }); + + it('should reject the promise with the appropriate error if failure for token', function () { + var error = 'my error', + sender = new Sender('myKey'); + setArgs(error); + sender.send({}, [1], 0).then(function() { + chai.assert.fail(); + }).catch(function(err) { + expect(err).to.equal(error); + expect(requestStub.args.length).to.equal(1); + }); + }); + + it('should reject the promise with the appropriate error if failure for tokens', function () { + var error = 'my error', + sender = new Sender('myKey'); + setArgs(error); + return sender.send({}, [1, 2, 3], 0).then(function() { + chai.assert.fail(); + }).catch(function(err) { + expect(err).to.equal(error); + expect(requestStub.args.length).to.equal(1); + }); + }); + + it('should retry number of times passed into call and do exponential backoff', function () { + var start = new Date(); + var sender = new Sender('myKey'); + setArgs(null, { statusCode: 500 }); + return sender.send({ data: {}}, [1], { + retries: 1, + backoff: 200 + }).catch(function() {}).then(function() { + expect(requestStub.args.length).to.equal(2); + expect(new Date() - start).to.be.gte(200); + }); + }); + + it('should retry if not all regTokens were successfully sent', function () { + var sender = new Sender('myKey'); + setArgs( + null, + { statusCode: 200}, + [ + { results: [{}, { error: 'Unavailable' }, + { error: 'Unavailable' }]}, + { results: [ {}, { error: 'Unavailable' } ] }, + { results: [ {} ] } + ]); + return sender.send({data: {}}, [1,2,3], { + retries: 5, + backoff: 100 + }).catch(function() {}).then(function() { + expect(requestStub.args.length).to.equal(3); + var requestOptions = requestStub.args[2][0]; + expect(requestOptions.json.registration_ids.length).to.equal(1); + expect(requestOptions.json.registration_ids[0]).to.equal(3); + }); + }); + + it('should retry all regTokens in event of an error', function () { + var sender = new Sender('myKey'); + setArgs('my error'); + return sender.send({ data: {}}, [1,2,3], 1).catch(function() {}).then(function() { + expect(requestStub.args.length).to.equal(2); + var requestOptions = requestStub.args[1][0]; + expect(requestOptions.json.registration_ids.length).to.equal(3); + }); + }); + + it('should update the failures and successes correctly when retrying', function () { + var sender = new Sender('myKey'); + setArgs(null, { statusCode: 200 }, [ + { success: 1, failure: 2, canonical_ids: 0, results: [ {}, { error: 'Unavailable' }, { error: 'Unavailable' } ] }, + { success: 1, canonical_ids: 1, failure: 0, results: [ {}, {} ] } + ]); + return sender.send({ data: {}}, [1,2,3], 3).then(function(response) { + expect(response.canonical_ids).to.equal(1); + expect(response.success).to.equal(2); + expect(response.failure).to.equal(0); + }); + }); + + it('should update the failures and successes correctly when retrying and failing some', function () { + var sender = new Sender('myKey'); + setArgs(null, { statusCode: 200 }, [ + { success: 0, failure: 3, canonical_ids: 0, results: [ { error: 'Unavailable' }, { error: 'Unavailable' }, { error: 'Unavailable' } ] }, + { success: 1, canonical_ids: 0, failure: 2, results: [ { error: 'Unavailable' }, { error: 'Unavailable' }, {} ] }, + { success: 0, canonical_ids: 0, failure: 2, results: [ { error: 'Unavailable' }, { error: 'Unavailable' } ] } + ]); + return sender.send({ data: {}}, [1,2,3], {retries: 2, backoff: 100}).then(function(response) { + expect(response.canonical_ids).to.equal(0); + expect(response.success).to.equal(1); + expect(response.failure).to.equal(2); + }); + }); + }); });