From f5fe616602a2f0bd5665a2502b3e21de79619c3d Mon Sep 17 00:00:00 2001 From: Ilir Nuhiu Date: Thu, 1 Dec 2016 18:44:24 +0100 Subject: [PATCH 01/39] add: HTTP2 APNS --- lib/providers/apns.js | 239 +++++++++++++++-------- package.json | 2 +- test/apns.provider.test.js | 279 +++++++++++---------------- test/helpers/mockery/apns.mockery.js | 31 +-- test/push-notification.test.js | 116 +++++------ 5 files changed, 339 insertions(+), 328 deletions(-) diff --git a/lib/providers/apns.js b/lib/providers/apns.js index eab19d5..e5502d0 100644 --- a/lib/providers/apns.js +++ b/lib/providers/apns.js @@ -2,133 +2,197 @@ // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 - 'use strict'; var g = require('strong-globalize')(); var inherits = require('util').inherits; +var assert = require('assert'); var EventEmitter = require('events').EventEmitter; var debug = require('debug')('loopback:component:push:provider:apns'); var apn = require('apn'); +/** + * Provider used to distribute push notifications through Apple Push Notification Service. + * @param pushSettings + * @constructor + */ function ApnsProvider(pushSettings) { pushSettings = pushSettings || {}; + var settings = pushSettings.apns || {}; var pushOptions = settings.pushOptions || {}; - var feedbackOptions = settings.feedbackOptions || {}; - // Populate the shared cert/key data - if (settings.certData) { - pushOptions.cert = pushOptions.certData || settings.certData; - feedbackOptions.cert = feedbackOptions.certData || settings.certData; - } - if (settings.keyData) { - pushOptions.key = pushOptions.keyData || settings.keyData; - feedbackOptions.key = feedbackOptions.keyData || settings.keyData; + // is running sandbox / production + if (typeof settings.production === 'undefined') { + pushOptions.production = false; + } else { + pushOptions.production = settings.production; } - // Check the push mode production vs development - if (settings.production) { - pushOptions.production = true; - feedbackOptions.production = true; + // validate required properties + var errors = { + token: 'JWT Token must be defined, property "token" is undefined.', + bundle: 'Bundle should contain the bundle identifier of the app', - // Always override - pushOptions.gateway = 'gateway.push.apple.com'; - feedbackOptions.gateway = 'feedback.push.apple.com'; - if (pushOptions.port !== undefined) { - pushOptions.port = 2195; - } - if (feedbackOptions.port !== undefined) { - feedbackOptions.port = 2196; - } - } else { - pushOptions.production = false; - feedbackOptions.production = false; + keyId: 'Tokens property "keyId" must be set.', + key: 'Tokens property "key" must be set.', + teamId: 'Tokens property "teamId" must be set.', + }; - // Honor the gateway settings for testing - pushOptions.gateway = pushOptions.gateway || - 'gateway.sandbox.push.apple.com'; - feedbackOptions.gateway = feedbackOptions.gateway || - 'feedback.sandbox.push.apple.com'; - } + var u = 'undefined'; + + assert.notStrictEqual(typeof settings.token, u, errors.token); + assert.notStrictEqual(typeof settings.bundle, u, errors.bundle); + + assert.notStrictEqual(typeof settings.token.keyId, u, errors.keyId); + assert.notStrictEqual(typeof settings.token.key, u, errors.key); + assert.notStrictEqual(typeof settings.token.teamId, u, errors.teamId); + + // handle token & bundle configuration + pushOptions.token = settings.token; + pushOptions.bundle = settings.bundle; // Keep the options for testing verification this._pushOptions = pushOptions; - this._feedbackOptions = feedbackOptions; - this._setupPushConnection(pushOptions); - this._setupFeedback(feedbackOptions); + /** + * the connection property + * @type {null} + * @private + */ + var _connection = null; + + /** + * Sets the stored connection + * @param connect + */ + this.setConnection = function(connect) { + _connection = connect; + }; + + /** + * Gets the stored connection or null + * @returns {*} + */ + this.getConnection = function() { + return _connection; + }; + + /** + * Retrieves whether is connected or not + * @returns {boolean} + */ + this.getConnected = function() { + return _connection !== null; + }; + + debug('Initialize APNS'); } inherits(ApnsProvider, EventEmitter); exports = module.exports = ApnsProvider; -ApnsProvider.prototype._setupPushConnection = function(options) { - debug('setting up push connection', options); +/** + * Ensures that the push connection is created, if not done yet. + * @param options + * @returns {*} + * @private + */ +ApnsProvider.prototype._ensurePushConnection = function(options) { var self = this; - if (options && options.port === null) { - options.port = undefined; + + debug('Check whether connected', self.getConnected()); + + // already connection running, close + if (self.getConnected()) { + debug('Connection already established, do not reconnect'); + return; } + debug('setting up push connection', self.getConnected(), options); + + /** + * Error handler for connection errors + * @param err + */ function errorHandler(err) { debug('Cannot initialize APNS connection. %s', err.stack); self.emit('error', err); } - var connection; + /** + * Error handler for transmission errors + * @param code + * @param notification + * @param recipient + */ + function transmissionErrorHandler(code, notification, recipient) { + var err = new Error(g.f('Cannot send {{APNS}} notification: %s', code)); + self.emit(err, notification, recipient); + } + + // try to connect & create the provider try { - connection = new apn.Connection(options); + self.setConnection(new apn.Provider(options)); + + debug('created connection', self.getConnected()); } catch (e) { return errorHandler(e); } - connection.on('error', errorHandler); - connection.on('socketError', errorHandler); - connection.on('transmissionError', function(code, notification, recipient) { - var err = new Error(g.f('Cannot send {{APNS}} notification: %s', code)); - self.emit(err, notification, recipient); - }); + // handle errors, attach handlers to the events + self.getConnection().on('error', errorHandler); + self.getConnection().on('socketError', errorHandler); + self.getConnection().on('transmissionError', transmissionErrorHandler); - this._connection = connection; + debug('is connected', self.getConnected()); }; -ApnsProvider.prototype._setupFeedback = function(options) { - debug('setting up feedback connection', options); - if (!options) { - debug('Feedback channel is not enabled in the application settings.'); - return; - } - if (options && options.port === null) { - options.port = undefined; - } - +/*** + * Send push notification through APNs + * @param notification + * @param deviceToken + */ +ApnsProvider.prototype.pushNotification = function(notification, deviceToken) { var self = this; + var pushOptions = self._pushOptions; - function errorHandler(err) { - debug('Cannot initialize APNS feedback. %s', err.stack); - self.emit('error', err); - } + // node-apn has a bug rightnow.. after sending the first + // batch of notifications, the connection goes away + // so make sure we reconnect in those cases + self._ensurePushConnection(pushOptions); - try { - this._feedback = new apn.Feedback(options); - } catch (e) { - return errorHandler(e); - } + // Note parameters are described here: + // http://bit.ly/apns-notification-payload + var note = _createNotification(notification, pushOptions); - this._feedback.on('error', errorHandler); + debug('Pushing notification to %j:', deviceToken, note); + + self.getConnection().send(note, deviceToken).then(function(result) { + debug('Sent through APNs, got result', result); - this._feedback.on('feedback', function(devices) { - debug('Devices gone:', devices); - self.emit('devicesGone', devices); + // we get immediate feedback from APNs + // distribute to notify everybody that there are devices unreachable + if (result.failed.length > 0) { + self.emit('devicesGone', _extractDeviceTokens(result.failed)); + } + }, function() { + debug('There was an error while sending', arguments); }); }; -ApnsProvider.prototype.pushNotification = function(notification, deviceToken) { - // Note parameters are described here: - // http://bit.ly/apns-notification-payload +/** + * Creates new apn notification object + * + * @param notification + * @param pushOptions + * @private + */ +function _createNotification(notification, pushOptions) { var note = new apn.Notification(); + note.expiry = notification.getTimeToLiveInSecondsFromNow() || note.expiry; note.badge = notification.badge; note.sound = notification.sound; @@ -138,10 +202,31 @@ ApnsProvider.prototype.pushNotification = function(notification, deviceToken) { note.urlArgs = notification.urlArgs; note.payload = {}; + // the topic is necessary to identify + // the app which receives this notification + note.topic = pushOptions.bundle; + + // custom stuff which will be added to the payload Object.keys(notification).forEach(function(key) { note.payload[key] = notification[key]; }); - debug('Pushing notification to %j:', deviceToken, note); - this._connection.pushNotification(note, deviceToken); -}; + return note; +} + +/** + * Extract the plain tokens from a list of failed devices + * @param failed + * @private + */ +function _extractDeviceTokens(failed) { + var tokens = []; + + failed.forEach(function(device) { + var token = device.device; + + tokens.push(token); + }); + + return tokens; +} diff --git a/package.json b/package.json index 8dba21d..161fb8a 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "node": ">=4" }, "dependencies": { - "apn": "^1.7.5", + "apn": "^v2.1.2", "async": "^1.5.2", "debug": "^2.2.0", "lodash": "^3.10.1", diff --git a/test/apns.provider.test.js b/test/apns.provider.test.js index 8cc7fc3..e0256c9 100644 --- a/test/apns.provider.test.js +++ b/test/apns.provider.test.js @@ -2,7 +2,6 @@ // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 - 'use strict'; var fs = require('fs'); @@ -12,6 +11,16 @@ var mockery = require('./helpers/mockery').apns; var objectMother = require('./helpers/object-mother'); var aDeviceToken = 'a-device-token'; +var defaultConfiguration = { + apns: { + token: { + keyId: 'key_id', + key: 'key', + teamId: 'team_id', + }, + bundle: 'ch.test.app', + }, +}; describe('APNS provider', function() { var provider; @@ -71,26 +80,34 @@ describe('APNS provider', function() { }); it('raises "devicesGone" event when feedback arrives', function(done) { - givenProviderWithConfig({ - apns: { - feedbackOptions: {}, - }, + givenProviderWithConfig(); + + var notification = aNotification({ + aKey: 'a-value', }); + var eventSpy = sinon.spy(); + provider.on('devicesGone', eventSpy); + provider.pushNotification(notification, aDeviceToken); - var devices = [aDeviceToken]; - mockery.emitFeedback(devices); + // HACK: Timeout does not work at this point + Promise.resolve(true).then(function() { + assert(eventSpy.called); + expect(eventSpy.args[0]).to.deep.equal([ + ['some_failing_device_token'], + ]); - expect(eventSpy.args[0]).to.deep.equal([devices]); - done(); + done(); + }, function() {}); }); it('converts expirationInterval to APNS expiry', function() { givenProviderWithConfig(); var notification = aNotification({ - expirationInterval: 1, /* second */ + expirationInterval: 1, + /* second */ }); provider.pushNotification(notification, aDeviceToken); @@ -122,204 +139,136 @@ describe('APNS provider', function() { }); }); - describe('in dev env', function() { - var notification; - - beforeEach(function setUp() { - notification = new Notification(); - }); - - it('emits "error" event when certData is invalid', function(done) { + describe('APNS settings', function() { + it('populates bundle/token data', function(done) { givenProviderWithConfig({ apns: { - certData: 'invalid-data', - pushOptions: { - gateway: '127.0.0.1', + token: { + keyId: 'my_key_id', + key: 'my_key', + teamId: 'team_id', }, + bundle: 'my_bundle_id', }, }); - var eventSpy = sinon.spy(); - provider.on('error', eventSpy); - provider.pushNotification(notification, aDeviceToken); + expect(provider._pushOptions).to.deep.equal({ + token: { + keyId: 'my_key_id', + key: 'my_key', + teamId: 'team_id', + }, + bundle: 'my_bundle_id', + production: false, + }); - // wait for the provider to attempt to connect - setTimeout(function() { - expect(eventSpy.called, 'error event should be emitted') - .to.equal(true); - var args = eventSpy.firstCall.args; - expect(args[0]).to.be.instanceOf(Error); - done(); - }, 50); + done(); }); - it('emits "error" when gateway cannot be reached', function(done) { - var CONNECT_TIMEOUT = 2000; - this.timeout(1.5 * CONNECT_TIMEOUT); + it('uses by default the sandbox mode', function(done) { givenProviderWithConfig({ apns: { - certData: objectMother.apnsDevCert(), - keyData: objectMother.apnsDevKey(), - pushOptions: { - gateway: '127.0.0.1', + token: { + keyId: 'my_key_id', + key: 'my_key', + teamId: 'team_id', }, + bundle: 'my_bundle_id', }, }); - var eventSpy = sinon.spy(); - provider.on('error', eventSpy); - - provider.pushNotification(notification, aDeviceToken); - - var start = Date.now(); - - // wait for the provider to attempt to connect - // periodically check whether it has happened yet - var interval = setInterval(function() { - var elapsed = Date.now() - start; - if (!eventSpy.called && elapsed < CONNECT_TIMEOUT) - return; // still connecting - - clearInterval(interval); - - expect(eventSpy.called, 'error event should be emitted') - .to.equal(true); - - var args = eventSpy.firstCall.args; - expect(args[0]).to.be.instanceOf(Error); - expect(args[0].code).to.equal('ECONNREFUSED'); - done(); - }, 50); + expect(provider._pushOptions.production === false); + done(); }); - }); - describe('APNS settings', function() { - it('populates cert/key data', function(done) { + it('uses production mode when set', function(done) { givenProviderWithConfig({ apns: { - certData: objectMother.apnsDevCert(), - keyData: objectMother.apnsDevKey(), - pushOptions: { - gateway: '127.0.0.1', + token: { + keyId: 'my_key_id', + key: 'my_key', + teamId: 'team_id', }, + bundle: 'my_bundle_id', }, + production: true, }); - expect(provider._pushOptions).to.deep.equal({ - cert: objectMother.apnsDevCert(), - key: objectMother.apnsDevKey(), - gateway: '127.0.0.1', - production: false, - }); - expect(provider._feedbackOptions).to.deep.equal({ - cert: objectMother.apnsDevCert(), - key: objectMother.apnsDevKey(), - gateway: 'feedback.sandbox.push.apple.com', - production: false, - }); + + expect(provider._pushOptions.production === true); done(); }); - it('populates dev gateways without overriding', function(done) { + + it('uses sandbox mode when set', function(done) { givenProviderWithConfig({ apns: { - pushOptions: { - gateway: 'push.test.com', - port: 1111, - }, - feedbackOptions: { - gateway: 'feedback.test.com', - port: 1112, + token: { + keyId: 'my_key_id', + key: 'my_key', + teamId: 'team_id', }, + bundle: 'my_bundle_id', }, - }); - expect(provider._pushOptions).to.deep.equal({ - gateway: 'push.test.com', - port: 1111, - production: false, - }); - expect(provider._feedbackOptions).to.deep.equal({ - gateway: 'feedback.test.com', - port: 1112, production: false, }); + + expect(provider._pushOptions.production === false); done(); }); - it('populates dev gateways', function(done) { - givenProviderWithConfig({ - apns: { - // intentionally omit the pushOptions for test - /* - pushOptions: { - }, - */ - feedbackOptions: { - interval: 300, + + it('reports error when bundle is not specified', function(done) { + var test = function() { + givenProviderWithConfig({ + apns: { + token: { + keyId: 'my_key_id', + key: 'my_key', + teamId: 'team_id', + }, }, - }, - }); - expect(provider._pushOptions).to.deep.equal({ - gateway: 'gateway.sandbox.push.apple.com', - production: false, - }); - expect(provider._feedbackOptions).to.deep.equal({ - gateway: 'feedback.sandbox.push.apple.com', - interval: 300, - production: false, - }); + }); + }; + + assert.throws(test, Error, 'Error thrown'); done(); }); - it('populates prod gateways', function(done) { - givenProviderWithConfig({ - apns: { - production: true, - pushOptions: {}, - feedbackOptions: { - interval: 300, - production: false, - }, - }, - }); - expect(provider._pushOptions).to.deep.equal({ - gateway: 'gateway.push.apple.com', - production: true, - }); - expect(provider._feedbackOptions).to.deep.equal({ - gateway: 'feedback.push.apple.com', - interval: 300, - production: true, - }); + + it('reports error when token is not specified', function(done) { + var test = function() { + givenProviderWithConfig({ + bundle: 'the_bundle', + }); + }; + + assert.throws(test, Error, 'Error thrown'); done(); }); - it('override prod gateways', function(done) { - givenProviderWithConfig({ - apns: { - production: true, - pushOptions: { - gateway: 'invalid', - port: 1111, - }, - feedbackOptions: { - gateway: 'invalid', - port: 1112, - interval: 300, + + it('reports error when token is missing a property', function(done) { + var test = function() { + givenProviderWithConfig({ + token: { + keyId: 'key_id', + key: 'key', }, - }, - }); - expect(provider._pushOptions).to.deep.equal({ - gateway: 'gateway.push.apple.com', - port: 2195, - production: true, - }); - expect(provider._feedbackOptions).to.deep.equal({ - gateway: 'feedback.push.apple.com', - port: 2196, - interval: 300, - production: true, - }); + bundle: 'the_bundle', + }); + }; + + assert.throws(test, Error, 'Error thrown'); done(); }); }); + /** + * Creates a provider with specified configuration. If configuration is left empty, a default one is created. + * @param pushSettings + */ function givenProviderWithConfig(pushSettings) { + // use a sensible default if nothing was specified + if (typeof pushSettings === 'undefined') { + pushSettings = defaultConfiguration; + } + provider = new ApnsProvider(pushSettings); } diff --git a/test/helpers/mockery/apns.mockery.js b/test/helpers/mockery/apns.mockery.js index 50aa80a..01ec139 100644 --- a/test/helpers/mockery/apns.mockery.js +++ b/test/helpers/mockery/apns.mockery.js @@ -2,7 +2,6 @@ // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 - 'use strict'; // This module provides a mocked environment where APN connections @@ -48,7 +47,7 @@ mockery.emitFeedback = function(devices) { * @returns {Array.} */ mockery.firstPushNotificationArgs = function() { - return mockery.pushNotification.firstCall.args; + return mockery.send.firstCall.args; }; var apnsSnapshot = {}; @@ -67,23 +66,25 @@ exports.setUp = function() { defaultExports[key] = exports[key]; } - mockery.pushNotification = sinon.spy(); + var expectedResponse = { + failed: [ + { + device: 'some_failing_device_token', + }, + ], + }; + + mockery.send = sinon.spy(function() { + return Promise.resolve(expectedResponse); + }); - apn.Connection = apn.connection = function(opts) { + apn.Provider = apn.provider = function(opts) { mockery.connectionOptions = opts; + var conn = new EventEmitter(); - conn.pushNotification = mockery.pushNotification; - return conn; - }; + conn.send = mockery.send; - apn.Feedback = apn.feedback = function(opts) { - mockery.feedbackOptions = opts; - var feedback = new EventEmitter(); - mockery.emitFeedback = function(devices) { - if (!(devices instanceof Array)) devices = [devices]; - feedback.emit('feedback', devices); - }; - return feedback; + return conn; }; }; diff --git a/test/push-notification.test.js b/test/push-notification.test.js index e51a5ea..7ac5a8d 100644 --- a/test/push-notification.test.js +++ b/test/push-notification.test.js @@ -2,83 +2,59 @@ // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 - 'use strict'; -var PushModel = PushConnector.createPushModel({dataSource: ds}); - -var objectMother = require('./helpers/object-mother'); +var PushModel = PushConnector.createPushModel({ + dataSource: ds, +}); describe('PushNotification', function() { it('registers a new installation', function(done) { - // Sign up an application - Application.register('test-user', 'TestApp', - { - description: 'My test mobile application', - pushSettings: { - apns: { - certData: objectMother.apnsDevCert(), - keyData: objectMother.apnsDevKey(), - pushOptions: { - }, - feedbackOptions: { - batchFeedback: true, - interval: 300, - }, + // Sign up an application + Application.register('test-user', 'TestApp', { + description: 'My test mobile application', + pushSettings: { + apns: { + token: { + keyId: 'key_id', + key: 'test/fixtures/APNs_token_key.p8', + teamId: 'team_id', }, + bundle: 'ch.test.app', }, - }, function(err, result) { - if (err) { - throw err; - } - var application = result; - - Installation.destroyAll(function(err, result) { - // console.log('Adding a test record'); - Installation.create({ - appId: application.id, - userId: 'raymond', - deviceToken: '75624450 3c9f95b4 9d7ff821 20dc193c a1e3a7cb ' + - '56f60c2e f2a19241 e8f33305', - deviceType: 'ios', - created: new Date(), - modified: new Date(), - status: 'Active', - }, function(err, result) { - if (err) { - console.error(err); - } else { - // console.log('Registration record is created: ', result); - } - - PushModel.dataSource.connector.applicationsCache.set( - application.id, - { - memory: { - pushNotification: function(notification, deviceToken) { - // console.log(notification, deviceToken); - assert.equal(deviceToken, result.deviceToken); - done(); - }, - }, - } - ); - - var note = new Notification(); - - // Expires 1 hour from now. - note.expirationInterval = Math.floor(Date.now() / 1000) + 3600; - note.badge = 5; - note.sound = 'ping.aiff'; - note.alert = '\uD83D\uDCE7 \u2709 ' + 'Hello'; - note.messageFrom = 'Ray'; - - PushModel.notifyById( - result.id, - note, - function(err) { if (err) throw err; done(); } - ); - }); + }, + }, function(err, result) { + if (err) { + throw err; + } + + var application = result; + var deviceToken = '6676119dc1ee264f7a32429c56c4e51b0a8b5673d1' + + 'd55c431d720bb60b0381d3'; + + Installation.destroyAll(function(err, result) { + // console.log('Adding a test record'); + Installation.create({ + appId: application.id, + userId: 'raymond', + deviceToken: deviceToken, + deviceType: 'ios', + created: new Date(), + modified: new Date(), + status: 'Active', + }, function(err, result) { + if (err) { + console.error(err); + + throw err; + } else { + expect(result.userId === 'raymond'); + expect(result.deviceToken === deviceToken); + expect(result.deviceType === 'ios'); + + done(); + } }); }); + }); }); }); From e1f219defe150223bf822874d1512f76dcf41f4c Mon Sep 17 00:00:00 2001 From: Siddhi Pai Date: Mon, 5 Dec 2016 23:19:20 -0800 Subject: [PATCH 02/39] Update paid support URL --- .github/ISSUE_TEMPLATE.md | 36 ++++++++++++++++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 24 +++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..ccc915a --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,36 @@ + + +### Bug or feature request + + + +- [ ] Bug +- [ ] Feature request + +### Description of feature (or steps to reproduce if bug) + + + +### Link to sample repo to reproduce issue (if bug) + + + +### Expected result + + + +### Actual result (if bug) + + + +### Additional information (Node.js version, LoopBack version, etc) + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..d2b240f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ +### Description + + +#### Related issues + + + +- None + +### Checklist + + + +- [ ] New tests added or existing tests modified to cover all changes +- [ ] Code conforms with the [style + guide](http://loopback.io/doc/en/contrib/style-guide.html) From b4c4b8074d825709c7a2b451a1d609671918d857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Wed, 21 Dec 2016 12:29:29 +0100 Subject: [PATCH 03/39] 3.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update paid support URL (Siddhi Pai) * add: HTTP2 APNS (Ilir Nuhiu) * Fix timeout in tests on Windows (Miroslav Bajtos) * Drop support for Node v0.10 & v0.12 (Miroslav Bajtoš) * Start the development of the next major version (Miroslav Bajtoš) * Remove Gruntfile and grunt deps (Miroslav Bajtoš) * Update README.md (Simon Ho) * Use eslint in favour of JSHint (#129) (Simon Ho) * Update translation files - round#2 (Candy) * Add translated files (gunjpan) * Update deps to LB 3.0.0 RC (Miroslav Bajtoš) * Use loopback@3.0.0-alpha for running the tests. (Miroslav Bajtoš) --- CHANGES.md | 16 +++++++++++++++- package.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f05ee1b..b1080a7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,20 @@ -2016-10-14, Version 1.7.0 +2016-12-21, Version 3.0.0 ========================= + * Update paid support URL (Siddhi Pai) + + * add: HTTP2 APNS (Ilir Nuhiu) + + * Fix timeout in tests on Windows (Miroslav Bajtos) + + * Drop support for Node v0.10 & v0.12 (Miroslav Bajtoš) + + * Start the development of the next major version (Miroslav Bajtoš) + + * Remove Gruntfile and grunt deps (Miroslav Bajtoš) + + * Update README.md (Simon Ho) + * Use eslint in favour of JSHint (#129) (Simon Ho) * Update translation files - round#2 (Candy) diff --git a/package.json b/package.json index 161fb8a..b43cbd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.0.0-alpha.1", + "version": "3.0.0", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs", From f4b1d33bda0c0bddf93873837ed926d097cc5924 Mon Sep 17 00:00:00 2001 From: Zak Barbuto Date: Tue, 7 Feb 2017 13:26:41 +1030 Subject: [PATCH 04/39] Add fcm support with addNotification --- lib/providers/gcm.js | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/providers/gcm.js b/lib/providers/gcm.js index fa93062..e086f36 100644 --- a/lib/providers/gcm.js +++ b/lib/providers/gcm.js @@ -90,5 +90,8 @@ GcmProvider.prototype._createMessage = function(notification) { } }); + message.addNotification('title', notification.messageFrom); + message.addNotification('body', notification.alert); + return message; }; diff --git a/package.json b/package.json index b43cbd0..c46731d 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "lodash": "^3.10.1", "mpns": "^2.1.0", "node-cache": "^3.2.1", - "node-gcm": "^0.14.0", + "node-gcm": "^0.14.4", "strong-globalize": "^2.6.2" }, "devDependencies": { From 79069ab23ff1772d83f10ae53d3f1b71c417773c Mon Sep 17 00:00:00 2001 From: Zak Barbuto Date: Tue, 7 Feb 2017 13:35:25 +1030 Subject: [PATCH 05/39] Add fcm tests --- test/gcm.provider.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/gcm.provider.test.js b/test/gcm.provider.test.js index 5c56eb2..25b4643 100644 --- a/test/gcm.provider.test.js +++ b/test/gcm.provider.test.js @@ -174,6 +174,21 @@ describe('GCM provider', function() { expect(message.params.delayWhileIdle, 'delayWhileIdle').to.equal(true); }); + it('adds appropriate fcm properties to the notification', function() { + var note = { + messageFrom: 'StrongLoop', + alert: 'Hello from StrongLoop', + }; + var notification = aNotification(note); + provider.pushNotification(notification, aDeviceToken); + + var message = mockery.firstPushNotificationArgs()[0]; + expect(message.params.notification).to.eql({ + title: note.messageFrom, + body: note.alert, + }); + }); + it('ignores Notification properties not applicable', function() { var notification = aNotification(objectMother.allNotificationProperties()); provider.pushNotification(notification, aDeviceToken); From a1373b1138c0c83bd91f4235a3d0ef55ad24292f Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Fri, 11 Aug 2017 13:08:26 -0400 Subject: [PATCH 06/39] update node version in travis --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a7f3157..dd338dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - "0.12" - - "0.11" - - "iojs" + - "4" + - "6" + - "8" From d3fd38631f2abcf902fe6e86d828947a6799f60c Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Mon, 24 Jul 2017 19:47:22 -0400 Subject: [PATCH 07/39] Add CODEOWNERS file --- CODEOWNERS | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..344b37a --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,6 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners, +# the last matching pattern has the most precendence. + +# Core team members from IBM +* @superkhau @raymondfeng From 3c69a8178e7573e0ed6ae91f4c0aa668e85d317e Mon Sep 17 00:00:00 2001 From: Sakib Hasan Date: Tue, 15 Aug 2017 16:00:23 -0400 Subject: [PATCH 08/39] create issue template --- .github/ISSUE_TEMPLATE.md | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ccc915a..795176c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,36 +1,37 @@ -### Bug or feature request +# Description/Steps to reproduce -- [ ] Bug -- [ ] Feature request - -### Description of feature (or steps to reproduce if bug) - - - -### Link to sample repo to reproduce issue (if bug) - - - -### Expected result +# Link to reproduction sandbox + -### Actual result (if bug) - - +# Expected result -### Additional information (Node.js version, LoopBack version, etc) + +# Additional information + From 142c97be92beb0dfc996c1d3f5b69d45cdabefeb Mon Sep 17 00:00:00 2001 From: Sakib Hasan Date: Tue, 15 Aug 2017 16:00:34 -0400 Subject: [PATCH 09/39] create pr template --- .github/PULL_REQUEST_TEMPLATE.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d2b240f..368cb4c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,17 +6,18 @@ -- None +- connect to ### Checklist - [ ] New tests added or existing tests modified to cover all changes From d37d9021a313ec7490a8ba3473f27ae2ab53b99e Mon Sep 17 00:00:00 2001 From: Kevin Delisle Date: Wed, 23 Aug 2017 08:27:24 -0400 Subject: [PATCH 10/39] Add stalebot configuration --- .github/stale.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..bebe60a --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,23 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 14 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - critical + - p1 + - major +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: > + This issue has been closed due to continued inactivity. Thank you for your understanding. + If you believe this to be in error, please contact one of the code owners, + listed in the `CODEOWNERS` file at the top-level of this repository. From a04758b9b6776a678900172fabfb52ac5711abf7 Mon Sep 17 00:00:00 2001 From: Zak Barbuto Date: Wed, 19 Apr 2017 15:37:23 +0930 Subject: [PATCH 11/39] Add support for additional notification props --- lib/providers/gcm.js | 6 ++++++ test/gcm.provider.test.js | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/providers/gcm.js b/lib/providers/gcm.js index e086f36..dbf0a98 100644 --- a/lib/providers/gcm.js +++ b/lib/providers/gcm.js @@ -92,6 +92,12 @@ GcmProvider.prototype._createMessage = function(notification) { message.addNotification('title', notification.messageFrom); message.addNotification('body', notification.alert); + ['icon', 'sound', 'badge', 'tag', 'color', 'click_action'] + .forEach(function(prop) { + if (notification[prop]) { + message.addNotification(prop, notification[prop]); + } + }); return message; }; diff --git a/test/gcm.provider.test.js b/test/gcm.provider.test.js index 25b4643..29eb70b 100644 --- a/test/gcm.provider.test.js +++ b/test/gcm.provider.test.js @@ -178,6 +178,12 @@ describe('GCM provider', function() { var note = { messageFrom: 'StrongLoop', alert: 'Hello from StrongLoop', + icon: 'logo.png', + sound: 'ping.tiff', + badge: 5, + tag: 'alerts', + color: '#ff0000', + 'click_action': 'OPEN_ACTIVITY_1', }; var notification = aNotification(note); provider.pushNotification(notification, aDeviceToken); @@ -186,6 +192,12 @@ describe('GCM provider', function() { expect(message.params.notification).to.eql({ title: note.messageFrom, body: note.alert, + icon: note.icon, + sound: note.sound, + badge: note.badge, + tag: note.tag, + color: note.color, + 'click_action': note.click_action, }); }); From 5924772296c5f3bfdca640b2515f719a7e738cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Mon, 18 Sep 2017 10:38:28 +0200 Subject: [PATCH 12/39] 3.1.0 * Add support for additional notification props (Zak Barbuto) * Add stalebot configuration (Kevin Delisle) * create pr template (Sakib Hasan) * create issue template (Sakib Hasan) * Add CODEOWNERS file (Diana Lau) * update node version in travis (Diana Lau) * Add fcm tests (Zak Barbuto) * Add fcm support with addNotification (Zak Barbuto) --- CHANGES.md | 20 ++++++++++++++++++++ package.json | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b1080a7..7815777 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,23 @@ +2017-09-18, Version 3.1.0 +========================= + + * Add support for additional notification props (Zak Barbuto) + + * Add stalebot configuration (Kevin Delisle) + + * create pr template (Sakib Hasan) + + * create issue template (Sakib Hasan) + + * Add CODEOWNERS file (Diana Lau) + + * update node version in travis (Diana Lau) + + * Add fcm tests (Zak Barbuto) + + * Add fcm support with addNotification (Zak Barbuto) + + 2016-12-21, Version 3.0.0 ========================= diff --git a/package.json b/package.json index c46731d..90a3541 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.0.0", + "version": "3.1.0", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs", From 0b719af18e5cbc63c41945513cc1fbd280b7efda Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Wed, 20 Sep 2017 13:58:30 -0400 Subject: [PATCH 13/39] add globalize string --- intl/en/messages.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/intl/en/messages.json b/intl/en/messages.json index 349d436..a155121 100644 --- a/intl/en/messages.json +++ b/intl/en/messages.json @@ -1,13 +1,13 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "notification must be an object", "0a11bfdd7655e825fbd1f998b2e24db9": "Empty payload", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens must be an array", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} error code: {0}, deviceToken: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Invalid field (empty)", "8bc87eb582af546d32505f2839234119": "Invalid payload", "db1e3eb9bf0934d72eb8b0b110281f39": "Invalid field: {0}", "e254b20a16461c7379202f6a5f8350cc": "Invalid variable type for ${{0}}", - "f5146dece5cd70db519daf8c7e6d8478": "The ${{0}} does not exist", - "f95c75ae3da1e88794fff4be7188f3dc": "Invalid value for `{0}`", "e9023b2ab0a052c9ccbd257198c7429f": "Cannot send {{APNS}} notification: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} error code: {0}, deviceToken: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "notification must be an object", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens must be an array" + "f5146dece5cd70db519daf8c7e6d8478": "The ${{0}} does not exist", + "f95c75ae3da1e88794fff4be7188f3dc": "Invalid value for `{0}`" } From 84d476c1177c63c3b7049f075c535edf089c0c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Mon, 25 Sep 2017 10:00:56 +0200 Subject: [PATCH 14/39] CODEOWNERS: add zbarbuto --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 344b37a..454854f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,4 +3,4 @@ # the last matching pattern has the most precendence. # Core team members from IBM -* @superkhau @raymondfeng +* @superkhau @raymondfeng @zbarbuto From 8e0a6344cbf811cf0d9fb7c3f8f58a50b80b8566 Mon Sep 17 00:00:00 2001 From: tangyinb Date: Mon, 9 Oct 2017 17:03:16 +0800 Subject: [PATCH 15/39] translation return for Q4 drop1 translation return for Q4 drop1 --- intl/de/messages.json | 10 +++++----- intl/es/messages.json | 10 +++++----- intl/fr/messages.json | 10 +++++----- intl/it/messages.json | 10 +++++----- intl/ja/messages.json | 10 +++++----- intl/ko/messages.json | 10 +++++----- intl/nl/messages.json | 10 +++++----- intl/pt/messages.json | 10 +++++----- intl/tr/messages.json | 10 +++++----- intl/zh-Hans/messages.json | 10 +++++----- intl/zh-Hant/messages.json | 10 +++++----- 11 files changed, 55 insertions(+), 55 deletions(-) diff --git a/intl/de/messages.json b/intl/de/messages.json index e37a797..ca09701 100644 --- a/intl/de/messages.json +++ b/intl/de/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "notification muss ein Objekt sein", "0a11bfdd7655e825fbd1f998b2e24db9": "Leere Nutzlast", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens muss ein Array sein", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}}-Fehlercode: {0}, deviceToken: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Ungültiges Feld (leer)", "8bc87eb582af546d32505f2839234119": "Ungültige Nutzlast", "db1e3eb9bf0934d72eb8b0b110281f39": "Ungültiges Feld: {0}", "e254b20a16461c7379202f6a5f8350cc": "Ungültiger Variablentyp für ${{0}}", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} ist nicht vorhanden", - "f95c75ae3da1e88794fff4be7188f3dc": "Ungültiger Wert für `{0}`", "e9023b2ab0a052c9ccbd257198c7429f": "{{APNS}}-Benachrichtigung kann nicht gesendet werden: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}}-Fehlercode: {0}, deviceToken: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "notification muss ein Objekt sein", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens muss ein Array sein" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} ist nicht vorhanden", + "f95c75ae3da1e88794fff4be7188f3dc": "Ungültiger Wert für `{0}`" } diff --git a/intl/es/messages.json b/intl/es/messages.json index ab47565..4a9249c 100644 --- a/intl/es/messages.json +++ b/intl/es/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "notification debe ser un objeto", "0a11bfdd7655e825fbd1f998b2e24db9": "Carga útil vacía", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens debe ser una matriz", + "704e7bf9910b8532f7c4889366fdcbac": "Código de error de {{GCM}}: {0}, deviceToken: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Campo no válido (vacío)", "8bc87eb582af546d32505f2839234119": "Carga útil no válida", "db1e3eb9bf0934d72eb8b0b110281f39": "Campo no válido: {0}", "e254b20a16461c7379202f6a5f8350cc": "Tipo de variable no válido para ${{0}}", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} no existe", - "f95c75ae3da1e88794fff4be7188f3dc": "Valor no válido para `{0}`", "e9023b2ab0a052c9ccbd257198c7429f": "No se puede enviar la notificación {{APNS}}: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "Código de error de {{GCM}}: {0}, deviceToken: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "notification debe ser un objeto", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens debe ser una matriz" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} no existe", + "f95c75ae3da1e88794fff4be7188f3dc": "Valor no válido para `{0}`" } diff --git a/intl/fr/messages.json b/intl/fr/messages.json index b0fdae9..a2b740b 100644 --- a/intl/fr/messages.json +++ b/intl/fr/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "la notification doit être un objet", "0a11bfdd7655e825fbd1f998b2e24db9": "Contenu vide", + "1ec12f42dab8979b9fc90a95a023419e": "les jetons d'unité doivent être un tableau", + "704e7bf9910b8532f7c4889366fdcbac": "Code d'erreur {{GCM}} : {0}, jeton d'unité : {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Zone non valide (vide)", "8bc87eb582af546d32505f2839234119": "Contenu non valide", "db1e3eb9bf0934d72eb8b0b110281f39": "Zone non valide : {0}", "e254b20a16461c7379202f6a5f8350cc": "Type de variable non valide pour ${{0}}", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} n'existe pas", - "f95c75ae3da1e88794fff4be7188f3dc": "Valeur non valide pour `{0}`", "e9023b2ab0a052c9ccbd257198c7429f": "Impossible d'envoyer la notification {{APNS}} : {0}", - "704e7bf9910b8532f7c4889366fdcbac": "Code d'erreur {{GCM}} : {0}, jeton d'unité : {1}", - "033d31e7bd62a420f23e4b154945a2b8": "la notification doit être un objet", - "1ec12f42dab8979b9fc90a95a023419e": "les jetons d'unité doivent être un tableau" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} n'existe pas", + "f95c75ae3da1e88794fff4be7188f3dc": "Valeur non valide pour `{0}`" } diff --git a/intl/it/messages.json b/intl/it/messages.json index c6f07b7..cdf9ff8 100644 --- a/intl/it/messages.json +++ b/intl/it/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "la notifica deve essere un oggetto", "0a11bfdd7655e825fbd1f998b2e24db9": "Payload vuoto", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens deve essere un array", + "704e7bf9910b8532f7c4889366fdcbac": "Codice di errore {{GCM}}: {0}, deviceToken: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Campo non valido (vuoto)", "8bc87eb582af546d32505f2839234119": "Payload non valido", "db1e3eb9bf0934d72eb8b0b110281f39": "Campo non valido: {0}", "e254b20a16461c7379202f6a5f8350cc": "Tipo di variabile non valido per ${{0}}", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} non esiste", - "f95c75ae3da1e88794fff4be7188f3dc": "Valore non valido per `{0}`", "e9023b2ab0a052c9ccbd257198c7429f": "Impossibile inviare la notifica {{APNS}}: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "Codice di errore {{GCM}}: {0}, deviceToken: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "la notifica deve essere un oggetto", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens deve essere un array" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} non esiste", + "f95c75ae3da1e88794fff4be7188f3dc": "Valore non valido per `{0}`" } diff --git a/intl/ja/messages.json b/intl/ja/messages.json index 47f2375..d6e0de2 100644 --- a/intl/ja/messages.json +++ b/intl/ja/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "通知はオブジェクトでなければなりません", "0a11bfdd7655e825fbd1f998b2e24db9": "空のペイロード", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens は配列でなければなりません", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} エラー・コード: {0}、deviceToken: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "無効なフィールド (空)", "8bc87eb582af546d32505f2839234119": "無効なペイロード", "db1e3eb9bf0934d72eb8b0b110281f39": "無効なフィールド: {0}", "e254b20a16461c7379202f6a5f8350cc": "${{0}} の変数型が無効です", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} は存在しません", - "f95c75ae3da1e88794fff4be7188f3dc": "`{0}` の値が無効です", "e9023b2ab0a052c9ccbd257198c7429f": "{{APNS}} 通知を送信できません: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} エラー・コード: {0}、deviceToken: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "通知はオブジェクトでなければなりません", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens は配列でなければなりません" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} は存在しません", + "f95c75ae3da1e88794fff4be7188f3dc": "`{0}` の値が無効です" } diff --git a/intl/ko/messages.json b/intl/ko/messages.json index 627b0f4..274a1cb 100644 --- a/intl/ko/messages.json +++ b/intl/ko/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "알림은 오브젝트여야 함", "0a11bfdd7655e825fbd1f998b2e24db9": "비어 있는 페이로드", + "1ec12f42dab8979b9fc90a95a023419e": "디바이스 토큰은 배열이어야 함", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} 오류 코드: {0}, 디바이스 토큰: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "올바르지 않은 필드(비어 있음)", "8bc87eb582af546d32505f2839234119": "올바르지 않은 페이로드", "db1e3eb9bf0934d72eb8b0b110281f39": "올바르지 않은 필드: {0}", "e254b20a16461c7379202f6a5f8350cc": "${{0}}의 올바르지 않은 변수 유형", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}}이(가) 없음", - "f95c75ae3da1e88794fff4be7188f3dc": "`{0}`의 올바르지 않은 값", "e9023b2ab0a052c9ccbd257198c7429f": "{{APNS}} 알림을 보낼 수 없음: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} 오류 코드: {0}, 디바이스 토큰: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "알림은 오브젝트여야 함", - "1ec12f42dab8979b9fc90a95a023419e": "디바이스 토큰은 배열이어야 함" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}}이(가) 없음", + "f95c75ae3da1e88794fff4be7188f3dc": "`{0}`의 올바르지 않은 값" } diff --git a/intl/nl/messages.json b/intl/nl/messages.json index 1973162..be3064a 100644 --- a/intl/nl/messages.json +++ b/intl/nl/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "melding moet een object zijn", "0a11bfdd7655e825fbd1f998b2e24db9": "Lege payload", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens moeten een array zijn", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}}-foutcode: {0}, deviceToken: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Ongeldig veld (leeg)", "8bc87eb582af546d32505f2839234119": "Ongeldige payload", "db1e3eb9bf0934d72eb8b0b110281f39": "Ongeldig veld: {0}", "e254b20a16461c7379202f6a5f8350cc": "Ongeldig type variabele voor ${{0}}", - "f5146dece5cd70db519daf8c7e6d8478": "Het item ${{0}} bestaat niet", - "f95c75ae3da1e88794fff4be7188f3dc": "Ongeldige waarde voor '{0}'", "e9023b2ab0a052c9ccbd257198c7429f": "Kan {{APNS}}-melding niet verzenden: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}}-foutcode: {0}, deviceToken: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "melding moet een object zijn", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens moeten een array zijn" + "f5146dece5cd70db519daf8c7e6d8478": "Het item ${{0}} bestaat niet", + "f95c75ae3da1e88794fff4be7188f3dc": "Ongeldige waarde voor '{0}'" } diff --git a/intl/pt/messages.json b/intl/pt/messages.json index 0e771d8..2b09250 100644 --- a/intl/pt/messages.json +++ b/intl/pt/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "notificação deve ser um objeto", "0a11bfdd7655e825fbd1f998b2e24db9": "Carga útil vazia", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens deve ser uma matriz", + "704e7bf9910b8532f7c4889366fdcbac": "Código de erro de {{GCM}}: {0}, deviceToken: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Campo inválido (vazio)", "8bc87eb582af546d32505f2839234119": "Carga útil inválida", "db1e3eb9bf0934d72eb8b0b110281f39": "Campo inválido: {0}", "e254b20a16461c7379202f6a5f8350cc": "Tipo de variável inválido para ${{0}}", - "f5146dece5cd70db519daf8c7e6d8478": "O ${{0}} não existe", - "f95c75ae3da1e88794fff4be7188f3dc": "Valor inválido para `{0}`", "e9023b2ab0a052c9ccbd257198c7429f": "Não é possível enviar notificação de {{APNS}}: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "Código de erro de {{GCM}}: {0}, deviceToken: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "notificação deve ser um objeto", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens deve ser uma matriz" + "f5146dece5cd70db519daf8c7e6d8478": "O ${{0}} não existe", + "f95c75ae3da1e88794fff4be7188f3dc": "Valor inválido para `{0}`" } diff --git a/intl/tr/messages.json b/intl/tr/messages.json index 692a568..b9f2cf5 100644 --- a/intl/tr/messages.json +++ b/intl/tr/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "bildirim bir nesne olmalıdır", "0a11bfdd7655e825fbd1f998b2e24db9": "Boş bilgi yükü", + "1ec12f42dab8979b9fc90a95a023419e": "aygıt belirteçleri bir dizi olmalıdır", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} hata kodu: {0}, aygıt belirteci: {1}", "71a2b601d1e750aa05c7a28ed76b9973": "Geçersiz alan (empty)", "8bc87eb582af546d32505f2839234119": "Geçersiz bilgi yükü", "db1e3eb9bf0934d72eb8b0b110281f39": "Geçersiz alan: {0}", "e254b20a16461c7379202f6a5f8350cc": "${{0}} için geçersiz değişken tipi", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} yok", - "f95c75ae3da1e88794fff4be7188f3dc": "`{0}` için geçersiz değer", "e9023b2ab0a052c9ccbd257198c7429f": "{{APNS}} bildirimi gönderilemiyor: {0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} hata kodu: {0}, aygıt belirteci: {1}", - "033d31e7bd62a420f23e4b154945a2b8": "bildirim bir nesne olmalıdır", - "1ec12f42dab8979b9fc90a95a023419e": "aygıt belirteçleri bir dizi olmalıdır" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} yok", + "f95c75ae3da1e88794fff4be7188f3dc": "`{0}` için geçersiz değer" } diff --git a/intl/zh-Hans/messages.json b/intl/zh-Hans/messages.json index 116b36e..ee2f145 100644 --- a/intl/zh-Hans/messages.json +++ b/intl/zh-Hans/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "通知必须是对象", "0a11bfdd7655e825fbd1f998b2e24db9": "空的有效内容", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens 必须是数组", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} 错误代码:{0},deviceToken:{1}", "71a2b601d1e750aa05c7a28ed76b9973": "无效字段(空)", "8bc87eb582af546d32505f2839234119": "无效的有效内容", "db1e3eb9bf0934d72eb8b0b110281f39": "无效字段:{0}", "e254b20a16461c7379202f6a5f8350cc": "${{0}} 的变量类型无效", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} 不存在", - "f95c75ae3da1e88794fff4be7188f3dc": "“{0}”的值无效", "e9023b2ab0a052c9ccbd257198c7429f": "无法发送 {{APNS}} 通知:{0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} 错误代码:{0},deviceToken:{1}", - "033d31e7bd62a420f23e4b154945a2b8": "通知必须是对象", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens 必须是数组" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} 不存在", + "f95c75ae3da1e88794fff4be7188f3dc": "“{0}”的值无效" } diff --git a/intl/zh-Hant/messages.json b/intl/zh-Hant/messages.json index 5954186..b6cb2cf 100644 --- a/intl/zh-Hant/messages.json +++ b/intl/zh-Hant/messages.json @@ -1,14 +1,14 @@ { + "033d31e7bd62a420f23e4b154945a2b8": "notification 必須是物件", "0a11bfdd7655e825fbd1f998b2e24db9": "有效負載空白", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens 必須是陣列", + "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} 錯誤碼:{0},deviceToken:{1}", "71a2b601d1e750aa05c7a28ed76b9973": "欄位無效(空白)", "8bc87eb582af546d32505f2839234119": "有效負載無效", "db1e3eb9bf0934d72eb8b0b110281f39": "無效欄位:{0}", "e254b20a16461c7379202f6a5f8350cc": "${{0}} 的變數類型無效", - "f5146dece5cd70db519daf8c7e6d8478": "${{0}} 不存在", - "f95c75ae3da1e88794fff4be7188f3dc": "`{0}` 的值無效", "e9023b2ab0a052c9ccbd257198c7429f": "無法傳送 {{APNS}} 通知:{0}", - "704e7bf9910b8532f7c4889366fdcbac": "{{GCM}} 錯誤碼:{0},deviceToken:{1}", - "033d31e7bd62a420f23e4b154945a2b8": "notification 必須是物件", - "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens 必須是陣列" + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} 不存在", + "f95c75ae3da1e88794fff4be7188f3dc": "`{0}` 的值無效" } From 7d4795dc9ceb5fa87d93e8e4814396e7281c47b7 Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Tue, 17 Oct 2017 11:27:29 -0400 Subject: [PATCH 16/39] 3.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * translation return for Q4 drop1 (tangyinb) * CODEOWNERS: add zbarbuto (Miroslav Bajtoš) * add globalize string (Diana Lau) --- CHANGES.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 7815777..0cdf16f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,13 @@ +2017-10-17, Version 3.2.0 +========================= + + * translation return for Q4 drop1 (tangyinb) + + * CODEOWNERS: add zbarbuto (Miroslav Bajtoš) + + * add globalize string (Diana Lau) + + 2017-09-18, Version 3.1.0 ========================= diff --git a/package.json b/package.json index 90a3541..e67893d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.1.0", + "version": "3.2.0", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs", From fb116f9a64f3378cb07f3a1e934f68bbc68dbb3b Mon Sep 17 00:00:00 2001 From: Zak Barbuto Date: Mon, 27 Nov 2017 13:04:00 +1030 Subject: [PATCH 17/39] Add support for data-only notifications Closes #165 --- lib/providers/gcm.js | 16 +++++++++++++--- test/gcm.provider.test.js | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/providers/gcm.js b/lib/providers/gcm.js index dbf0a98..387ee8a 100644 --- a/lib/providers/gcm.js +++ b/lib/providers/gcm.js @@ -90,14 +90,24 @@ GcmProvider.prototype._createMessage = function(notification) { } }); - message.addNotification('title', notification.messageFrom); - message.addNotification('body', notification.alert); + addKey(message, 'title', notification, 'messageFrom'); + addKey(message, 'body', notification, 'alert'); + ['icon', 'sound', 'badge', 'tag', 'color', 'click_action'] .forEach(function(prop) { if (notification[prop]) { - message.addNotification(prop, notification[prop]); + addKey(message, prop, notification); } }); return message; }; + +function addKey(message, key, notification, prop) { + prop = prop || key; + if (notification.dataOnly) { + message.addData(key, notification[prop]); + } else { + message.addNotification(key, notification[prop]); + } +} diff --git a/test/gcm.provider.test.js b/test/gcm.provider.test.js index 29eb70b..26951f0 100644 --- a/test/gcm.provider.test.js +++ b/test/gcm.provider.test.js @@ -223,6 +223,31 @@ describe('GCM provider', function() { expect(message.params.data).to.deep.equal({aFalse: false, aTrue: true}); }); + it('supports data-only notifications', function() { + var note = { + messageFrom: 'StrongLoop', + alert: 'Hello from StrongLoop', + icon: 'logo.png', + sound: 'ping.tiff', + badge: 5, + dataOnly: true, + }; + var notification = aNotification(note); + provider.pushNotification(notification, aDeviceToken); + + var message = mockery.firstPushNotificationArgs()[0]; + expect(message.params.data).to.eql({ + messageFrom: 'StrongLoop', + alert: 'Hello from StrongLoop', + title: 'StrongLoop', + body: 'Hello from StrongLoop', + icon: 'logo.png', + sound: 'ping.tiff', + badge: 5, + dataOnly: true, + }); + }); + function givenProviderWithConfig(pushSettings) { pushSettings = extend({}, pushSettings); pushSettings.gcm = extend({}, pushSettings.gcm); From 18cd953344f958359afbdfb23ff58a8fdc7790b2 Mon Sep 17 00:00:00 2001 From: Zak Barbuto Date: Thu, 31 Aug 2017 16:33:57 +0930 Subject: [PATCH 18/39] Fix missing title on iOS notifications Expects a 'title' property https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW5 --- lib/providers/apns.js | 2 +- test/apns.provider.test.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/providers/apns.js b/lib/providers/apns.js index e5502d0..5a7d764 100644 --- a/lib/providers/apns.js +++ b/lib/providers/apns.js @@ -200,8 +200,8 @@ function _createNotification(notification, pushOptions) { note.category = notification.category; note.contentAvailable = notification.contentAvailable; note.urlArgs = notification.urlArgs; + note.title = notification.title || notification.messageFrom; note.payload = {}; - // the topic is necessary to identify // the app which receives this notification note.topic = pushOptions.bundle; diff --git a/test/apns.provider.test.js b/test/apns.provider.test.js index e0256c9..7caa6de 100644 --- a/test/apns.provider.test.js +++ b/test/apns.provider.test.js @@ -59,6 +59,8 @@ describe('APNS provider', function() { givenProviderWithConfig(); var notification = aNotification({ + alert: 'You have a message from StrongLoop', + messageFrom: 'StrongLoop', contentAvailable: true, category: 'my-category', urlArgs: ['foo', 'bar'], @@ -75,6 +77,9 @@ describe('APNS provider', function() { expect(payload.aps.category, 'aps.category').to.equal('my-category'); expect(payload.aps['url-args'], 'aps.url-args').to.have.length(2); expect(payload.arbitrary, 'arbitrary').to.equal('baz'); + expect(payload.aps.alert.title, 'title').to.equal('StrongLoop'); + expect(payload.aps.alert.body, 'body').to + .equal('You have a message from StrongLoop'); done(); }); From 17d23980548d94da62a7c8824e1639d0f745e27a Mon Sep 17 00:00:00 2001 From: Serge Bornow Date: Fri, 5 Jan 2018 18:43:48 -0500 Subject: [PATCH 19/39] Update README.md Correcting link to examples for backend, ios and android --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9527401..010cf44 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ providers such as APNS, GCM, and MPNS ### Node.js server -This module includes an [example LoopBack server application](https://github.com/strongloop/loopback-component-push/tree/master/example/server-2.0). +This module includes an [example LoopBack server application](https://github.com/strongloop/loopback-example-push). To run it, use these commands: @@ -53,12 +53,12 @@ MONGODB=mongodb://localhost/demo node app ### iOS client -The [iOS example app](https://github.com/strongloop/loopback-component-push/tree/master/example/ios) +The [iOS example app](https://github.com/strongloop/loopback-example-push/tree/master/ios) uses the LoopBack iOS SDK to enable and handle push notifications. ### Android client -The [Android example app](https://github.com/strongloop/loopback-component-push/tree/master/example/android) +The [Android example app](https://github.com/strongloop/loopback-example-push/tree/master/android) uses the LoopBack Android SDK to enable and handle push notifications. ## References From 482c83a7ba3fe6c141b23d7739231c20b1b87e68 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 26 Apr 2018 08:51:55 -0700 Subject: [PATCH 20/39] 3.3.0 * Update README.md (Serge Bornow) * Fix missing title on iOS notifications (Zak Barbuto) * Add support for data-only notifications (Zak Barbuto) --- CHANGES.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 0cdf16f..579cd1f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,13 @@ +2018-04-26, Version 3.3.0 +========================= + + * Update README.md (Serge Bornow) + + * Fix missing title on iOS notifications (Zak Barbuto) + + * Add support for data-only notifications (Zak Barbuto) + + 2017-10-17, Version 3.2.0 ========================= diff --git a/package.json b/package.json index e67893d..b080a2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.2.0", + "version": "3.3.0", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs", From e27c46386dbe2fdd868f992db3057dc725b29830 Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Sat, 28 Apr 2018 17:45:29 -0400 Subject: [PATCH 21/39] chore: update lodash version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b080a2f..1f0c826 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "apn": "^v2.1.2", "async": "^1.5.2", "debug": "^2.2.0", - "lodash": "^3.10.1", + "lodash": "^4.17.10", "mpns": "^2.1.0", "node-cache": "^3.2.1", "node-gcm": "^0.14.4", From 411d16a53d6256c5bbeeb022ee41cabd16e2764a Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Tue, 8 May 2018 12:57:08 -0400 Subject: [PATCH 22/39] 3.3.1 * chore: update lodash version (Diana Lau) --- CHANGES.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 579cd1f..aa538d3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +2018-05-08, Version 3.3.1 +========================= + + * chore: update lodash version (Diana Lau) + + 2018-04-26, Version 3.3.0 ========================= diff --git a/package.json b/package.json index 1f0c826..cdf6891 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.3.0", + "version": "3.3.1", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs", From d1bd929cf176fc27e645c0732391a646338833f7 Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Tue, 15 May 2018 20:20:03 -0400 Subject: [PATCH 23/39] chore:update dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cdf6891..6705651 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "lodash": "^4.17.10", "mpns": "^2.1.0", "node-cache": "^3.2.1", - "node-gcm": "^0.14.4", + "node-gcm": "^1.0.0", "strong-globalize": "^2.6.2" }, "devDependencies": { From d28ffd1bf5575c053764b5b74c7c471bcd1ac15e Mon Sep 17 00:00:00 2001 From: candytangnb Date: Fri, 29 Jun 2018 00:01:36 -0400 Subject: [PATCH 24/39] [WebFM] cs/pl/ru translation cs/pl/ru translation check-in by YI TANG (tangyinb@cn.ibm.com) using WebFM tool. --- intl/cs/messages.json | 14 ++++++++++++++ intl/pl/messages.json | 14 ++++++++++++++ intl/ru/messages.json | 14 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 intl/cs/messages.json create mode 100644 intl/pl/messages.json create mode 100644 intl/ru/messages.json diff --git a/intl/cs/messages.json b/intl/cs/messages.json new file mode 100644 index 0000000..21a920e --- /dev/null +++ b/intl/cs/messages.json @@ -0,0 +1,14 @@ +{ + "033d31e7bd62a420f23e4b154945a2b8": "oznámení musí být objekt", + "0a11bfdd7655e825fbd1f998b2e24db9": "Prázdný informační obsah", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens musí být pole", + "704e7bf9910b8532f7c4889366fdcbac": "Kód chyby {{GCM}}: {0}, deviceToken: {1}", + "71a2b601d1e750aa05c7a28ed76b9973": "Neplatné pole (prázdné)", + "8bc87eb582af546d32505f2839234119": "Neplatný informační obsah", + "db1e3eb9bf0934d72eb8b0b110281f39": "Neplatné pole: {0}", + "e254b20a16461c7379202f6a5f8350cc": "Neplatný typ proměnné pro ${{0}}", + "e9023b2ab0a052c9ccbd257198c7429f": "Nelze odeslat oznámení {{APNS}}: {0}", + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} neexistuje", + "f95c75ae3da1e88794fff4be7188f3dc": "Neplatná hodnota pro `{0}`" +} + diff --git a/intl/pl/messages.json b/intl/pl/messages.json new file mode 100644 index 0000000..45c6dd3 --- /dev/null +++ b/intl/pl/messages.json @@ -0,0 +1,14 @@ +{ + "033d31e7bd62a420f23e4b154945a2b8": "powiadomienie musi być obiektem", + "0a11bfdd7655e825fbd1f998b2e24db9": "Pusty ładunek", + "1ec12f42dab8979b9fc90a95a023419e": "Parametr deviceTokens musi być tablicą", + "704e7bf9910b8532f7c4889366fdcbac": "Kod błędu {{GCM}}: {0}, deviceToken: {1}", + "71a2b601d1e750aa05c7a28ed76b9973": "Niepoprawne pole (puste)", + "8bc87eb582af546d32505f2839234119": "Niepoprawny ładunek", + "db1e3eb9bf0934d72eb8b0b110281f39": "Niepoprawne pole: {0}", + "e254b20a16461c7379202f6a5f8350cc": "Niepoprawny typ zmiennej dla ${{0}}", + "e9023b2ab0a052c9ccbd257198c7429f": "Nie można wysłać powiadomienia {{APNS}}: {0}", + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} nie istnieje", + "f95c75ae3da1e88794fff4be7188f3dc": "Niepoprawna wartość `{0}`" +} + diff --git a/intl/ru/messages.json b/intl/ru/messages.json new file mode 100644 index 0000000..49328e6 --- /dev/null +++ b/intl/ru/messages.json @@ -0,0 +1,14 @@ +{ + "033d31e7bd62a420f23e4b154945a2b8": "уведомление должно быть объектом", + "0a11bfdd7655e825fbd1f998b2e24db9": "Пустое значение полезной нагрузки", + "1ec12f42dab8979b9fc90a95a023419e": "deviceTokens должен быть массивом", + "704e7bf9910b8532f7c4889366fdcbac": "Код ошибки {{GCM}}: {0}, deviceToken: {1}", + "71a2b601d1e750aa05c7a28ed76b9973": "Недопустимое поле (пустое)", + "8bc87eb582af546d32505f2839234119": "Недопустимая полезная нагрузка", + "db1e3eb9bf0934d72eb8b0b110281f39": "Недопустимое поле: {0}", + "e254b20a16461c7379202f6a5f8350cc": "Недопустимый тип переменной для ${{0}}", + "e9023b2ab0a052c9ccbd257198c7429f": "Не удалось отправить уведомление {{APNS}}: {0}", + "f5146dece5cd70db519daf8c7e6d8478": "${{0}} не существует", + "f95c75ae3da1e88794fff4be7188f3dc": "Недопустимое значение для `{0}`" +} + From 5302f1edfc5aacb266a7f47ff45a4aa704b9d93e Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Mon, 9 Jul 2018 11:11:01 -0400 Subject: [PATCH 25/39] chore: update node dependencies --- .travis.yml | 2 +- package.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index dd338dd..ceafed9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - "4" - "6" - "8" + - "10" diff --git a/package.json b/package.json index 6705651..724f52d 100644 --- a/package.json +++ b/package.json @@ -16,17 +16,17 @@ "posttest": "npm run lint" }, "engines": { - "node": ">=4" + "node": ">=6" }, "dependencies": { "apn": "^v2.1.2", "async": "^1.5.2", - "debug": "^2.2.0", + "debug": "^3.1.0", "lodash": "^4.17.10", "mpns": "^2.1.0", "node-cache": "^3.2.1", "node-gcm": "^1.0.0", - "strong-globalize": "^2.6.2" + "strong-globalize": "^4.1.1" }, "devDependencies": { "chai": "^2.3.0", @@ -35,7 +35,7 @@ "eslint-config-loopback": "^4.0.0", "loopback": "^3.0.0", "loopback-connector-mongodb": "^1.8.0", - "mocha": "^2.2.0", + "mocha": "^4.0.0", "should": "^6.0.0", "sinon": "^1.14.0" }, From 4b512a6d3a03c40326a806299f2ed3e866aeadc7 Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Mon, 9 Jul 2018 17:07:09 -0400 Subject: [PATCH 26/39] fix lint errors --- lib/payload.js | 2 +- lib/providers/gcm.js | 2 +- lib/push-connector.js | 2 +- lib/push-manager.js | 28 +- package.json | 4 +- test/apns.provider.test.js | 51 +- test/common.js | 30 -- test/device-registration.test.js | 84 +-- test/gcm.provider.test.js | 98 ++-- test/helpers/test-data-builder.js | 2 +- test/mocha.opts | 1 - test/push-manager.test.js | 831 ++++++++++++++++-------------- test/push-notification.test.js | 108 ++-- 13 files changed, 690 insertions(+), 553 deletions(-) delete mode 100644 test/common.js diff --git a/lib/payload.js b/lib/payload.js index 05e9024..9696826 100644 --- a/lib/payload.js +++ b/lib/payload.js @@ -46,7 +46,7 @@ function Payload(data) { break; default: if ( - (_ref = key.split('.', 2), prefix = _ref[0], subkey = _ref[1], _ref) + (_ref = key.split('.', 2), prefix = _ref[0], subkey = _ref[1], _ref) .length === 2) { this[prefix][subkey] = value; } else { diff --git a/lib/providers/gcm.js b/lib/providers/gcm.js index 387ee8a..d74ea00 100644 --- a/lib/providers/gcm.js +++ b/lib/providers/gcm.js @@ -42,7 +42,7 @@ GcmProvider.prototype.pushNotification = function(notification, deviceToken) { var code; result.results.forEach(function(value, index) { code = value && value.error; - if (code === 'NotRegistered' || code === 'InvalidRegistration') { + if (code === 'NotRegistered' || code === 'InvalidRegistration') { debug('Device %j is no longer registered.', registrationIds[index]); devicesGoneRegistrationIds.push(registrationIds[index]); } else if (code) { diff --git a/lib/push-connector.js b/lib/push-connector.js index a7b5e78..b08b564 100644 --- a/lib/push-connector.js +++ b/lib/push-connector.js @@ -15,7 +15,7 @@ var PushManager = require('./push-manager'); exports.initialize = function(dataSource, callback) { var settings = dataSource.settings || {}; - // Create an instance of the APNSManager + // Create an instance of the APNSManager var connector = new PushManager(settings); dataSource.connector = connector; dataSource.connector.dataSource = dataSource; diff --git a/lib/push-manager.js b/lib/push-manager.js index c585990..31f0055 100644 --- a/lib/push-manager.js +++ b/lib/push-manager.js @@ -258,7 +258,7 @@ PushManager.prototype.notifyById = function(installationId, notification, cb) { * @param {function(Error=)} cb */ PushManager.prototype.notifyByQuery = function(installationQuery, notification, -cb) { + cb) { assert.ok(cb, 'callback should be defined'); var self = this; var filter = {where: installationQuery}; @@ -320,7 +320,7 @@ PushManager.prototype.notify = function(installation, notification, cb) { ); }; - /** +/** * Push notification to installations for given devices tokens, device type and app. * * @param {String} appId application id @@ -330,7 +330,7 @@ PushManager.prototype.notify = function(installation, notification, cb) { * @param {function(Error=)} cb */ PushManager.prototype.notifyMany = function(appId, deviceType, deviceTokens, -notification, cb) { + notification, cb) { assert(appId, 'appId should be defined'); assert(deviceType, 'deviceType should be defined'); assert(cb, 'callback should be defined'); @@ -343,8 +343,8 @@ notification, cb) { return cb(new Error(g.f('deviceTokens must be an array'))); } - // Normalize the notification from a plain object - // for remote calls + // Normalize the notification from a plain object + // for remote calls if (!(notification instanceof this.Notification)) { notification = new this.Notification(notification); if (!notification.isValid()) { @@ -353,15 +353,15 @@ notification, cb) { } this.configureApplication( - appId, - deviceType, - function(err, provider) { - if (err) { return cb(err); } - - provider.pushNotification(notification, deviceTokens); - cb(); - } - ); + appId, + deviceType, + function(err, provider) { + if (err) { return cb(err); } + + provider.pushNotification(notification, deviceTokens); + cb(); + } + ); }; /*! diff --git a/package.json b/package.json index 724f52d..d32148b 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,8 @@ "devDependencies": { "chai": "^2.3.0", "deep-extend": "^0.4.0", - "eslint": "^2.13.1", - "eslint-config-loopback": "^4.0.0", + "eslint": "^4.0.0", + "eslint-config-loopback": "^10.0.0", "loopback": "^3.0.0", "loopback-connector-mongodb": "^1.8.0", "mocha": "^4.0.0", diff --git a/test/apns.provider.test.js b/test/apns.provider.test.js index 7caa6de..5973567 100644 --- a/test/apns.provider.test.js +++ b/test/apns.provider.test.js @@ -6,6 +6,10 @@ var fs = require('fs'); var path = require('path'); +var expect = require('chai').expect; +var assert = require('assert'); +var sinon = require('sinon'); +var loopback = require('loopback'); var ApnsProvider = require('../lib/providers/apns'); var mockery = require('./helpers/mockery').apns; var objectMother = require('./helpers/object-mother'); @@ -22,6 +26,20 @@ var defaultConfiguration = { }, }; +var ds = loopback.createDataSource('db', { + connector: loopback.Memory, +}); + +var Application = loopback.Application; +Application.attachTo(ds); + +var PushConnector = require('../'); +var Installation = PushConnector.Installation; +Installation.attachTo(ds); + +var Notification = PushConnector.Notification; +Notification.attachTo(ds); + describe('APNS provider', function() { var provider; @@ -72,14 +90,17 @@ describe('APNS provider', function() { var note = apnArgs[0]; var payload = note.toJSON(); - expect(payload.aps['content-available'], 'aps.content-available').to - .equal(1); + expect( + payload.aps['content-available'], + 'aps.content-available' + ).to.equal(1); expect(payload.aps.category, 'aps.category').to.equal('my-category'); expect(payload.aps['url-args'], 'aps.url-args').to.have.length(2); expect(payload.arbitrary, 'arbitrary').to.equal('baz'); expect(payload.aps.alert.title, 'title').to.equal('StrongLoop'); - expect(payload.aps.alert.body, 'body').to - .equal('You have a message from StrongLoop'); + expect(payload.aps.alert.body, 'body').to.equal( + 'You have a message from StrongLoop' + ); done(); }); @@ -97,14 +118,17 @@ describe('APNS provider', function() { provider.pushNotification(notification, aDeviceToken); // HACK: Timeout does not work at this point - Promise.resolve(true).then(function() { - assert(eventSpy.called); - expect(eventSpy.args[0]).to.deep.equal([ - ['some_failing_device_token'], - ]); - - done(); - }, function() {}); + Promise.resolve(true).then( + function() { + assert(eventSpy.called); + expect(eventSpy.args[0]).to.deep.equal([ + ['some_failing_device_token'], + ]); + + done(); + }, + function() {} + ); }); it('converts expirationInterval to APNS expiry', function() { @@ -136,7 +160,8 @@ describe('APNS provider', function() { givenProviderWithConfig(); var notification = aNotification( - objectMother.allNotificationProperties()); + objectMother.allNotificationProperties() + ); provider.pushNotification(notification, aDeviceToken); var note = mockery.firstPushNotificationArgs()[0]; diff --git a/test/common.js b/test/common.js deleted file mode 100644 index 0ac57e6..0000000 --- a/test/common.js +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright IBM Corp. 2015. All Rights Reserved. -// Node module: loopback-component-push -// This file is licensed under the Artistic License 2.0. -// License text available at https://opensource.org/licenses/Artistic-2.0 - -'use strict'; - -/* exported global */ -global.chai = require('chai'); -global.should = require('chai').should(); -global.expect = require('chai').expect; -global.AssertionError = require('chai').AssertionError; -global.loopback = require('loopback'); -global.assert = require('assert'); - -global.sinon = require('sinon'); - -global.ds = global.loopback.createDataSource('db', { - connector: global.loopback.Memory, -}); - -global.Application = global.loopback.Application; -global.Application.attachTo(global.ds); - -global.PushConnector = require('../'); -global.Installation = PushConnector.Installation; -global.Installation.attachTo(global.ds); - -global.Notification = PushConnector.Notification; -global.Notification.attachTo(global.ds); diff --git a/test/device-registration.test.js b/test/device-registration.test.js index c74d1ba..c4b483d 100644 --- a/test/device-registration.test.js +++ b/test/device-registration.test.js @@ -4,50 +4,66 @@ // License text available at https://opensource.org/licenses/Artistic-2.0 'use strict'; +var assert = require('assert'); +var loopback = require('loopback'); + +var ds = loopback.createDataSource('db', { + connector: loopback.Memory, +}); +var PushConnector = require('../'); +var Installation = PushConnector.Installation; +Installation.attachTo(ds); describe('Installation', function() { var registration = null; it('registers a new installation', function(done) { - var token = '75624450 3c9f95b4 9d7ff821 20dc193c a1e3a7cb 56f60c2e ' + + var token = + '75624450 3c9f95b4 9d7ff821 20dc193c a1e3a7cb 56f60c2e ' + 'f2a19241 e8f33305'; - Installation.create({ - appId: 'MyLoopbackApp', - appVersion: '1', - userId: 'raymond', - deviceToken: token, - deviceType: 'ios', - created: new Date(), - modified: new Date(), - status: 'Active', - }, function(err, result) { - if (err) { - console.error(err); - done(err, result); - return; - } else { - var reg = result; - assert.equal(reg.appId, 'MyLoopbackApp'); - assert.equal(reg.userId, 'raymond'); - assert.equal(reg.deviceType, 'ios'); - assert.equal(reg.deviceToken, token); - - assert(reg.created); - assert(reg.modified); - - registration = reg; - - Installation.findByApp('ios', 'MyLoopbackApp', function(err, results) { - assert(!err); - assert.equal(results.length, 1); - var reg = results[0]; + Installation.create( + { + appId: 'MyLoopbackApp', + appVersion: '1', + userId: 'raymond', + deviceToken: token, + deviceType: 'ios', + created: new Date(), + modified: new Date(), + status: 'Active', + }, + function(err, result) { + if (err) { + console.error(err); + done(err, result); + return; + } else { + var reg = result; assert.equal(reg.appId, 'MyLoopbackApp'); assert.equal(reg.userId, 'raymond'); assert.equal(reg.deviceType, 'ios'); assert.equal(reg.deviceToken, token); - done(err, results); - }); + + assert(reg.created); + assert(reg.modified); + + registration = reg; + + Installation.findByApp('ios', 'MyLoopbackApp', function( + err, + results + ) { + assert(!err); + assert.equal(results.length, 1); + var reg = results[0]; + assert.equal(reg.appId, 'MyLoopbackApp'); + assert.equal(reg.userId, 'raymond'); + assert.equal(reg.deviceType, 'ios'); + assert.equal(reg.deviceToken, token); + done(err, results); + }); + } } - }); + ); }); }); diff --git a/test/gcm.provider.test.js b/test/gcm.provider.test.js index 26951f0..4d41519 100644 --- a/test/gcm.provider.test.js +++ b/test/gcm.provider.test.js @@ -5,10 +5,13 @@ 'use strict'; +var expect = require('chai').expect; +var sinon = require('sinon'); var extend = require('util')._extend; var GcmProvider = require('../lib/providers/gcm'); var mockery = require('./helpers/mockery').gcm; var objectMother = require('./helpers/object-mother'); +var loopback = require('loopback'); var aDeviceToken = 'a-device-token'; var aDeviceTokenList = [ @@ -19,12 +22,28 @@ var aDeviceTokenList = [ 'fifth-device-token', ]; +var ds = loopback.createDataSource('db', { + connector: loopback.Memory, +}); + +var Application = loopback.Application; +Application.attachTo(ds); + +var PushConnector = require('../'); +var Installation = PushConnector.Installation; +Installation.attachTo(ds); + +var Notification = PushConnector.Notification; +Notification.attachTo(ds); + describe('GCM provider', function() { var provider; beforeEach(mockery.setUp); beforeEach(setUpFakeTimers); - beforeEach(function() { givenProviderWithConfig(); }); + beforeEach(function() { + givenProviderWithConfig(); + }); afterEach(tearDownFakeTimers); afterEach(mockery.tearDown); @@ -39,12 +58,14 @@ describe('GCM provider', function() { var gcmArgs = mockery.firstPushNotificationArgs(); var msg = gcmArgs[0]; - expect(msg.params.collapseKey, 'collapseKey').to - .equal(undefined); + expect(msg.params.collapseKey, 'collapseKey').to.equal(undefined); expect(msg.params.delayWhileIdle, 'delayWhileIdle').to.equal(undefined); expect(msg.params.timeToLive, 'timeToLive').to.equal(undefined); - expect(msg.params.data, 'data').to - .deep.equal({aKey: 'a-value', alert: 'alert message', badge: 1}); + expect(msg.params.data, 'data').to.deep.equal({ + aKey: 'a-value', + alert: 'alert message', + badge: 1, + }); expect(gcmArgs[1]).to.deep.equal([aDeviceToken]); done(); @@ -58,14 +79,15 @@ describe('GCM provider', function() { provider.pushNotification(aNotification(), aDeviceToken); - expect(eventSpy.calledOnce, 'error should be emitted once').to - .equal(true); + expect(eventSpy.calledOnce, 'error should be emitted once').to.equal( + true + ); expect(eventSpy.args[0]).to.deep.equal([anError]); }); it('emits "error" event when GCM returns error result', function() { // This is a real result returned by GCM - var errorResult = aGcmResult([{'error': 'MismatchSenderId'}]); + var errorResult = aGcmResult([{error: 'MismatchSenderId'}]); mockery.pushNotificationCallbackArgs = [null, errorResult]; @@ -73,19 +95,22 @@ describe('GCM provider', function() { provider.pushNotification(aNotification(), aDeviceToken); - expect(eventSpy.calledOnce, 'error should be emitted once').to - .equal(true); + expect(eventSpy.calledOnce, 'error should be emitted once').to.equal( + true + ); expect(eventSpy.firstCall.args[0].message).to.contain('MismatchSenderId'); }); it('emits "devicesGone" when GCM returns NotRegistered', function(done) { - var errorResult = aGcmResult([{'error': 'NotRegistered'}]); + var errorResult = aGcmResult([{error: 'NotRegistered'}]); mockery.pushNotificationCallbackArgs = [null, errorResult]; var eventSpy = sinon.spy(); provider.on('devicesGone', eventSpy); - provider.on('error', function(err) { throw err; }); + provider.on('error', function(err) { + throw err; + }); provider.pushNotification(aNotification(), aDeviceToken); @@ -113,16 +138,19 @@ describe('GCM provider', function() { }); it('handles GCM response for multiple device tokens', function(done) { - var gcmError = new Error('GCM error code: MismatchSenderId, ' + + var gcmError = new Error( + 'GCM error code: MismatchSenderId, ' + 'deviceToken: third-device-token\nGCM error code: ' + - 'MismatchSenderId, deviceToken: fifth-device-token'); + 'MismatchSenderId, deviceToken: fifth-device-token' + ); var gcmResult = aGcmResult([ - {'error': 'InvalidRegistration'}, - {'message_id': '1234567890'}, - {'error': 'MismatchSenderId'}, - {'error': 'NotRegistered'}, - {'error': 'MismatchSenderId'}, + {error: 'InvalidRegistration'}, + // eslint-disable-next-line + { message_id: '1234567890' }, + {error: 'MismatchSenderId'}, + {error: 'NotRegistered'}, + {error: 'MismatchSenderId'}, ]); mockery.pushNotificationCallbackArgs = [null, gcmResult]; @@ -136,8 +164,9 @@ describe('GCM provider', function() { provider.pushNotification(aNotification(), aDeviceTokenList); var expectedIds = [aDeviceTokenList[0], aDeviceTokenList[3]]; - expect(eventSpy.calledOnce, 'error should be emitted once').to - .equal(true); + expect(eventSpy.calledOnce, 'error should be emitted once').to.equal( + true + ); expect(eventSpy.args[0][0]).to.deep.equal(expectedIds); done(); }); @@ -183,7 +212,8 @@ describe('GCM provider', function() { badge: 5, tag: 'alerts', color: '#ff0000', - 'click_action': 'OPEN_ACTIVITY_1', + // eslint-disable-next-line + click_action: 'OPEN_ACTIVITY_1', }; var notification = aNotification(note); provider.pushNotification(notification, aDeviceToken); @@ -197,7 +227,8 @@ describe('GCM provider', function() { badge: note.badge, tag: note.tag, color: note.color, - 'click_action': note.click_action, + // eslint-disable-next-line + click_action: note.click_action, }); }); @@ -206,8 +237,10 @@ describe('GCM provider', function() { provider.pushNotification(notification, aDeviceToken); var message = mockery.firstPushNotificationArgs()[0]; - expect(message.params.data).to - .deep.equal({alert: 'an-alert', badge: 1230001}); + expect(message.params.data).to.deep.equal({ + alert: 'an-alert', + badge: 1230001, + }); }); it('ignores Notification properties null or undefined', function() { @@ -253,7 +286,8 @@ describe('GCM provider', function() { pushSettings.gcm = extend({}, pushSettings.gcm); pushSettings.gcm.pushOptions = extend( {serverKey: 'a-test-server-key'}, - pushSettings.gcm.pushOptions); + pushSettings.gcm.pushOptions + ); provider = new GcmProvider(pushSettings); } @@ -272,11 +306,13 @@ describe('GCM provider', function() { }).length; return { - 'multicast_id': 5504081219335647631, - 'success': success, - 'failure': failure, - 'canonical_ids': 0, - 'results': results, + // eslint-disable-next-line + multicast_id: 5504081219335647631, + success: success, + failure: failure, + // eslint-disable-next-line + canonical_ids: 0, + results: results, }; } diff --git a/test/helpers/test-data-builder.js b/test/helpers/test-data-builder.js index e9a4e27..7d7c7e7 100644 --- a/test/helpers/test-data-builder.js +++ b/test/helpers/test-data-builder.js @@ -143,7 +143,7 @@ TestDataBuilder.prototype._gatherDefaultPropertyValues = function(Model) { case Date: result[name] = new Date( 2222, 12, 12, // yyyy, mm, dd - 12, 12, 12, // hh, MM, ss + 12, 12, 12, // hh, MM, ss ++valueCounter // milliseconds ); break; diff --git a/test/mocha.opts b/test/mocha.opts index 8382147..7b9581e 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,3 +1,2 @@ --timeout 30000 --reporter spec ---require test/common diff --git a/test/push-manager.test.js b/test/push-manager.test.js index e5de71f..95e309c 100644 --- a/test/push-manager.test.js +++ b/test/push-manager.test.js @@ -5,7 +5,9 @@ 'use strict'; var async = require('async'); - +var expect = require('chai').expect; +var loopback = require('loopback'); +var sinon = require('sinon'); var PushManager = require('../lib/push-manager'); var NodeCache = require('node-cache'); @@ -13,6 +15,23 @@ var mockery = require('./helpers/mockery').stub; var TestDataBuilder = require('./helpers/test-data-builder'); var ref = TestDataBuilder.ref; +var ds = loopback.createDataSource('db', { + connector: loopback.Memory, +}); + +// Application +var Application = loopback.Application; +Application.attachTo(ds); + +// Push Connector +var PushConnector = require('../'); +var Installation = PushConnector.Installation; +Installation.attachTo(ds); + +// Notification +var Notification = PushConnector.Notification; +Notification.attachTo(ds); + describe('PushManager', function() { beforeEach(mockery.setUp); beforeEach(Application.deleteAll.bind(Application)); @@ -30,91 +49,95 @@ describe('PushManager', function() { }); it('deletes devices no longer registered', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('application', Application, { - pushSettings: {stub: { }}, - }) - .define('installation', Installation, { - appId: ref('application.id'), - deviceType: mockery.deviceType, - }) - .buildTo(context, cb); - }, - - function configureProvider(cb) { - pushManager.configureApplication( - context.installation.appId, - context.installation.deviceType, - cb); - }, - - function act(cb) { - mockery.emitDevicesGone(context.installation.deviceToken); - - // Wait until the feedback is processed - // We can use process.nextTick because Memory store - // deletes the data within this event loop - process.nextTick(cb); - }, - - function verify(cb) { - Installation.find(function(err, result) { - if (err) return cb(err); - expect(result).to.have.length(0); - cb(); - }); - }, - ], done); - }); - - describe('.notify', function() { - it('should set device type/token from installation', function(done) { - async.series([ + async.series( + [ function arrange(cb) { new TestDataBuilder() .define('application', Application, { - pushSettings: {stub: { }}, - }) - // Note: the order in which the installations are created - // is important. - // The installation that should not receive the notification must - // be created first. This way the test fails when PushManager - // looks up the installation via - // `Installation.findOne({ deviceToken: token })` - .define('anotherDevice', Installation, { - appId: ref('application.id'), - deviceToken: 'a-device-token', - deviceType: 'another-device-type', + pushSettings: {stub: {}}, }) .define('installation', Installation, { appId: ref('application.id'), - deviceToken: 'a-device-token', deviceType: mockery.deviceType, }) .buildTo(context, cb); }, - function act(cb) { - pushManager.notify( - context.installation, - context.notification, + function configureProvider(cb) { + pushManager.configureApplication( + context.installation.appId, + context.installation.deviceType, cb ); }, + function act(cb) { + mockery.emitDevicesGone(context.installation.deviceToken); + + // Wait until the feedback is processed + // We can use process.nextTick because Memory store + // deletes the data within this event loop + process.nextTick(cb); + }, + function verify(cb) { - // Wait with the check to give the push manager some time - // to load all data and push the message - setTimeout(function() { - expect(mockery.firstPushNotificationArgs()).to.deep.equal( - [context.notification, context.installation.deviceToken] - ); + Installation.find(function(err, result) { + if (err) return cb(err); + expect(result).to.have.length(0); cb(); - }, 50); + }); }, - ], done); + ], + done + ); + }); + + describe('.notify', function() { + it('should set device type/token from installation', function(done) { + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('application', Application, { + pushSettings: {stub: {}}, + }) + // Note: the order in which the installations are created + // is important. + // The installation that should not receive the notification must + // be created first. This way the test fails when PushManager + // looks up the installation via + // `Installation.findOne({ deviceToken: token })` + .define('anotherDevice', Installation, { + appId: ref('application.id'), + deviceToken: 'a-device-token', + deviceType: 'another-device-type', + }) + .define('installation', Installation, { + appId: ref('application.id'), + deviceToken: 'a-device-token', + deviceType: mockery.deviceType, + }) + .buildTo(context, cb); + }, + + function act(cb) { + pushManager.notify(context.installation, context.notification, cb); + }, + + function verify(cb) { + // Wait with the check to give the push manager some time + // to load all data and push the message + setTimeout(function() { + expect(mockery.firstPushNotificationArgs()).to.deep.equal([ + context.notification, + context.installation.deviceToken, + ]); + cb(); + }, 50); + }, + ], + done + ); }); it('reports error on invalid notification', function(done) { @@ -131,361 +154,402 @@ describe('PushManager', function() { describe('.notifyById', function() { it('sends notification to the correct installation', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('application', Application, { - pushSettings: {stub: { }}, - }) - // Note: the order in which the installations are created - // is important. - // The installation that should not receive the notification must - // be created first. This way the test fails when PushManager - // looks up the installation via - // `Installation.findOne({ deviceToken: token })` - .define('anotherDevice', Installation, { - appId: ref('application.id'), - deviceToken: 'a-device-token', - deviceType: 'another-device-type', - }) - .define('installation', Installation, { - appId: ref('application.id'), - deviceToken: 'a-device-token', - deviceType: mockery.deviceType, - }) - .buildTo(context, cb); - }, - - function act(cb) { - pushManager.notifyById( - context.installation.id, - context.notification, - cb - ); - }, - - function verify(cb) { - // Wait with the check to give the push manager some time - // to load all data and push the message - setTimeout(function() { - expect(mockery.firstPushNotificationArgs()).to.deep.equal( - [context.notification, context.installation.deviceToken] + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('application', Application, { + pushSettings: {stub: {}}, + }) + // Note: the order in which the installations are created + // is important. + // The installation that should not receive the notification must + // be created first. This way the test fails when PushManager + // looks up the installation via + // `Installation.findOne({ deviceToken: token })` + .define('anotherDevice', Installation, { + appId: ref('application.id'), + deviceToken: 'a-device-token', + deviceType: 'another-device-type', + }) + .define('installation', Installation, { + appId: ref('application.id'), + deviceToken: 'a-device-token', + deviceType: mockery.deviceType, + }) + .buildTo(context, cb); + }, + + function act(cb) { + pushManager.notifyById( + context.installation.id, + context.notification, + cb ); - cb(); - }, 50); - }, - ], done); + }, + + function verify(cb) { + // Wait with the check to give the push manager some time + // to load all data and push the message + setTimeout(function() { + expect(mockery.firstPushNotificationArgs()).to.deep.equal([ + context.notification, + context.installation.deviceToken, + ]); + cb(); + }, 50); + }, + ], + done + ); }); it('reports error when installation was not found', function(done) { - async.series([ - function actAndVerify(cb) { - pushManager.notifyById( - 'unknown-installation-id', - context.notification, - verify - ); + async.series( + [ + function actAndVerify(cb) { + pushManager.notifyById( + 'unknown-installation-id', + context.notification, + verify + ); - function verify(err) { - expect(err).to.be.instanceOf(Error); - expect(err.details) - .to.have.property('installationId', 'unknown-installation-id'); - cb(); - } - }, - ], done); + function verify(err) { + expect(err).to.be.instanceOf(Error); + expect(err.details).to.have.property( + 'installationId', + 'unknown-installation-id' + ); + cb(); + } + }, + ], + done + ); }); it('reports error when application was not found', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('installation', Installation, {appId: 'unknown-app-id'}) - .buildTo(context, cb); - }, - - function actAndVerify(cb) { - pushManager.notifyById( - context.installation.id, - context.notification, - verify - ); + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('installation', Installation, {appId: 'unknown-app-id'}) + .buildTo(context, cb); + }, + + function actAndVerify(cb) { + pushManager.notifyById( + context.installation.id, + context.notification, + verify + ); - function verify(err) { - expect(err).to.be.instanceOf(Error); - expect(err.details) - .to.have.property('appId', 'unknown-app-id'); - cb(); - } - }, - ], done); + function verify(err) { + expect(err).to.be.instanceOf(Error); + expect(err.details).to.have.property('appId', 'unknown-app-id'); + cb(); + } + }, + ], + done + ); }); it('reports error when application has no pushSettings', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('application', Application, {pushSettings: null}) - .define('installation', Installation, { - appId: ref('application.id'), - deviceType: 'unknown-device-type', - }) - .buildTo(context, cb); - }, - - function actAndVerify(cb) { - pushManager.notifyById( - context.installation.id, - context.notification, - verify - ); + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('application', Application, {pushSettings: null}) + .define('installation', Installation, { + appId: ref('application.id'), + deviceType: 'unknown-device-type', + }) + .buildTo(context, cb); + }, + + function actAndVerify(cb) { + pushManager.notifyById( + context.installation.id, + context.notification, + verify + ); - function verify(err) { - expect(err).to.be.instanceOf(Error); - expect(err.details).to.have.property('application'); - cb(); - } - }, - ], done); + function verify(err) { + expect(err).to.be.instanceOf(Error); + expect(err.details).to.have.property('application'); + cb(); + } + }, + ], + done + ); }); it('reports error for unknown device type', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('application', Application, {pushSettings: {}}) - .define('installation', Installation, { - appId: ref('application.id'), - deviceType: 'unknown-device-type', - }) - .buildTo(context, cb); - }, - - function actAndVerify(cb) { - pushManager.notifyById( - context.installation.id, - context.notification, - verify - ); + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('application', Application, {pushSettings: {}}) + .define('installation', Installation, { + appId: ref('application.id'), + deviceType: 'unknown-device-type', + }) + .buildTo(context, cb); + }, + + function actAndVerify(cb) { + pushManager.notifyById( + context.installation.id, + context.notification, + verify + ); - function verify(err) { - expect(err).to.be.instanceOf(Error); - cb(); - } - }, - ], done); + function verify(err) { + expect(err).to.be.instanceOf(Error); + cb(); + } + }, + ], + done + ); }); it('emits error when push fails inside provider', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('application', Application, { - pushSettings: {stub: { }}, - }) - .define('installation', Installation, { - appId: ref('application.id'), - deviceToken: 'a-device-token', - deviceType: mockery.deviceType, - }) - .buildTo(context, cb); - }, - - function actAndVerify(cb) { - var errorCallback = sinon.spy(); - pushManager.on('error', errorCallback); - - mockery.pushNotification = function() { - this.emit('error', new Error('a test error')); - }; - - pushManager.notifyById( - context.installation.id, - context.notification, - function(err) { - if (err) throw err; - expect(errorCallback.calledOnce, 'error was emitted') - .to.equal(true); - cb(); - } - ); - }, - ], done); + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('application', Application, { + pushSettings: {stub: {}}, + }) + .define('installation', Installation, { + appId: ref('application.id'), + deviceToken: 'a-device-token', + deviceType: mockery.deviceType, + }) + .buildTo(context, cb); + }, + + function actAndVerify(cb) { + var errorCallback = sinon.spy(); + pushManager.on('error', errorCallback); + + mockery.pushNotification = function() { + this.emit('error', new Error('a test error')); + }; + + pushManager.notifyById( + context.installation.id, + context.notification, + function(err) { + if (err) throw err; + expect(errorCallback.calledOnce, 'error was emitted').to.equal( + true + ); + cb(); + } + ); + }, + ], + done + ); }); }); describe('.notifyByQuery', function() { it('sends notifications to the correct installations', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('application', Application, { - pushSettings: {stub: { }}, - }) - .define('myPhone', Installation, { - appId: ref('application.id'), - deviceToken: 'my-phone-token', - deviceType: mockery.deviceType, - userId: 'myself', - }) - .define('myOtherPhone', Installation, { - appId: ref('application.id'), - deviceToken: 'my-other-phone-token', - deviceType: mockery.deviceType, - userId: 'myself', - }) - .define('friendsPhone', Installation, { - appId: ref('application.id'), - deviceToken: 'friends-phone-token', - deviceType: mockery.deviceType, - userId: 'somebody else', - }) - .buildTo(context, cb); - }, - - function act(cb) { - pushManager.notifyByQuery( - {userId: 'myself'}, - context.notification, - cb - ); - }, - - function verify(cb) { - // Wait with the check to give the push manager some time - // to load all data and push the message - setTimeout(function() { - var callsArgs = mockery.pushNotification.args; - expect(callsArgs, 'number of notifications').to.have.length(2); - expect(callsArgs[0]).to.deep.equal( - [context.notification, context.myPhone.deviceToken] - ); - expect(callsArgs[1]).to.deep.equal( - [context.notification, context.myOtherPhone.deviceToken] + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('application', Application, { + pushSettings: {stub: {}}, + }) + .define('myPhone', Installation, { + appId: ref('application.id'), + deviceToken: 'my-phone-token', + deviceType: mockery.deviceType, + userId: 'myself', + }) + .define('myOtherPhone', Installation, { + appId: ref('application.id'), + deviceToken: 'my-other-phone-token', + deviceType: mockery.deviceType, + userId: 'myself', + }) + .define('friendsPhone', Installation, { + appId: ref('application.id'), + deviceToken: 'friends-phone-token', + deviceType: mockery.deviceType, + userId: 'somebody else', + }) + .buildTo(context, cb); + }, + + function act(cb) { + pushManager.notifyByQuery( + {userId: 'myself'}, + context.notification, + cb ); - cb(); - }, 50); - }, - ], done); + }, + + function verify(cb) { + // Wait with the check to give the push manager some time + // to load all data and push the message + setTimeout(function() { + var callsArgs = mockery.pushNotification.args; + expect(callsArgs, 'number of notifications').to.have.length(2); + expect(callsArgs[0]).to.deep.equal([ + context.notification, + context.myPhone.deviceToken, + ]); + expect(callsArgs[1]).to.deep.equal([ + context.notification, + context.myOtherPhone.deviceToken, + ]); + cb(); + }, 50); + }, + ], + done + ); }); it('reports error on non-object notifications', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('myPhone', Installation, { - userId: 'myself', - }) - .buildTo(context, cb); - }, - - function act(cb) { - pushManager.notifyByQuery( - {userId: 'myself'}, - 'invalid notification', // invalid - function(err) { - expect(err.message).to.equal('notification must be an object'); - cb(); - } - ); - }, - ], done); + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('myPhone', Installation, { + userId: 'myself', + }) + .buildTo(context, cb); + }, + + function act(cb) { + pushManager.notifyByQuery( + {userId: 'myself'}, + 'invalid notification', // invalid + function(err) { + expect(err.message).to.equal('notification must be an object'); + cb(); + } + ); + }, + ], + done + ); }); }); describe('.notifyMany', function() { it('sends notifications to the correct installations', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('application', Application, { - pushSettings: {stub: { }}, - }) - .define('firstPhone', Installation, { - appId: ref('application.id'), - deviceToken: 'first-phone-token', - deviceType: mockery.deviceType, - userId: 'myself', - }) - .define('secondPhone', Installation, { - appId: ref('application.id'), - deviceToken: 'second-phone-token', - deviceType: mockery.deviceType, - userId: 'myself', - }) - .define('thirdPhone', Installation, { - appId: ref('application.id'), - deviceToken: 'third-phone-token', - deviceType: mockery.deviceType, - userId: 'somebody else', - }) - .buildTo(context, cb); - }, - function act(cb) { - pushManager.notifyMany( + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('application', Application, { + pushSettings: {stub: {}}, + }) + .define('firstPhone', Installation, { + appId: ref('application.id'), + deviceToken: 'first-phone-token', + deviceType: mockery.deviceType, + userId: 'myself', + }) + .define('secondPhone', Installation, { + appId: ref('application.id'), + deviceToken: 'second-phone-token', + deviceType: mockery.deviceType, + userId: 'myself', + }) + .define('thirdPhone', Installation, { + appId: ref('application.id'), + deviceToken: 'third-phone-token', + deviceType: mockery.deviceType, + userId: 'somebody else', + }) + .buildTo(context, cb); + }, + function act(cb) { + pushManager.notifyMany( context.application.id, mockery.deviceType, ['first-phone-token', 'second-phone-token'], context.notification, cb - ); - }, - function verify(cb) { - // Wait with the check to give the push manager some time - // to load all data and push the message - setTimeout(function() { - var callsArgs = mockery.pushNotification.args; - - expect(callsArgs[0], 'number of arguments').to.have.length(2); - expect(callsArgs[0]).to.deep.equal([ - context.notification, - [context.firstPhone.deviceToken, context.secondPhone.deviceToken], - ]); - cb(); - }, 50); - }, - ], done); + ); + }, + function verify(cb) { + // Wait with the check to give the push manager some time + // to load all data and push the message + setTimeout(function() { + var callsArgs = mockery.pushNotification.args; + + expect(callsArgs[0], 'number of arguments').to.have.length(2); + expect(callsArgs[0]).to.deep.equal([ + context.notification, + [ + context.firstPhone.deviceToken, + context.secondPhone.deviceToken, + ], + ]); + cb(); + }, 50); + }, + ], + done + ); }); it('reports error if device token is not an array', function(done) { - async.series([ - function arrange(cb) { - new TestDataBuilder() - .define('myPhone', Installation, { - userId: 'myself', - }) - .buildTo(context, cb); - }, - function act(cb) { - pushManager.notifyMany( - '1', - 'ios', - 'invalid-phone-token', - context.notification, - function(err) { - expect(err.message).to.equal('deviceTokens must be an array'); - cb(); - } - ); - }, - ], done); + async.series( + [ + function arrange(cb) { + new TestDataBuilder() + .define('myPhone', Installation, { + userId: 'myself', + }) + .buildTo(context, cb); + }, + function act(cb) { + pushManager.notifyMany( + '1', + 'ios', + 'invalid-phone-token', + context.notification, + function(err) { + expect(err.message).to.equal('deviceTokens must be an array'); + cb(); + } + ); + }, + ], + done + ); }); it('reports error on non-object notifications', function(done) { - async.series([ - function verify(cb) { - pushManager.notifyMany( - '1', - 'ios', - ['phone-token'], - 'invalid-notification', - function(err) { - expect(err.message).to.equal('notification must be an object'); - cb(); - } - ); - }, - ], done); + async.series( + [ + function verify(cb) { + pushManager.notifyMany( + '1', + 'ios', + ['phone-token'], + 'invalid-notification', + function(err) { + expect(err.message).to.equal('notification must be an object'); + cb(); + } + ); + }, + ], + done + ); }); }); @@ -518,7 +582,7 @@ describe('PushManager', function() { function arrange(cb) { new TestDataBuilder() .define('application', Application, { - pushSettings: {stub: { }}, + pushSettings: {stub: {}}, }) .define('installation', Installation, { appId: ref('application.id'), @@ -531,13 +595,14 @@ describe('PushManager', function() { pushManager.configureApplication( context.application.id, context.installation.deviceType, - cb); + cb + ); }, function verify(cb) { - var cacheApp = pushManager - .applicationsCache - .get(context.installation.appId); + var cacheApp = pushManager.applicationsCache.get( + context.installation.appId + ); expect(cacheApp).to.have.property(context.installation.appId); }, ]); diff --git a/test/push-notification.test.js b/test/push-notification.test.js index 7ac5a8d..4f98a6d 100644 --- a/test/push-notification.test.js +++ b/test/push-notification.test.js @@ -3,58 +3,84 @@ // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 'use strict'; +var expect = require('chai').expect; +var loopback = require('loopback'); + +var ds = loopback.createDataSource('db', { + connector: loopback.Memory, +}); + +// Application +var Application = loopback.Application; +Application.attachTo(ds); + +// Push Connector +var PushConnector = require('../'); var PushModel = PushConnector.createPushModel({ dataSource: ds, }); +// Installation +var Installation = PushConnector.Installation; +Installation.attachTo(ds); + describe('PushNotification', function() { it('registers a new installation', function(done) { // Sign up an application - Application.register('test-user', 'TestApp', { - description: 'My test mobile application', - pushSettings: { - apns: { - token: { - keyId: 'key_id', - key: 'test/fixtures/APNs_token_key.p8', - teamId: 'team_id', + Application.register( + 'test-user', + 'TestApp', + { + description: 'My test mobile application', + pushSettings: { + apns: { + token: { + keyId: 'key_id', + key: 'test/fixtures/APNs_token_key.p8', + teamId: 'team_id', + }, + bundle: 'ch.test.app', }, - bundle: 'ch.test.app', }, }, - }, function(err, result) { - if (err) { - throw err; - } + function(err, result) { + if (err) { + throw err; + } + + var application = result; + var deviceToken = + '6676119dc1ee264f7a32429c56c4e51b0a8b5673d1' + + 'd55c431d720bb60b0381d3'; + + Installation.destroyAll(function(err, result) { + // console.log('Adding a test record'); + Installation.create( + { + appId: application.id, + userId: 'raymond', + deviceToken: deviceToken, + deviceType: 'ios', + created: new Date(), + modified: new Date(), + status: 'Active', + }, + function(err, result) { + if (err) { + console.error(err); - var application = result; - var deviceToken = '6676119dc1ee264f7a32429c56c4e51b0a8b5673d1' + - 'd55c431d720bb60b0381d3'; - - Installation.destroyAll(function(err, result) { - // console.log('Adding a test record'); - Installation.create({ - appId: application.id, - userId: 'raymond', - deviceToken: deviceToken, - deviceType: 'ios', - created: new Date(), - modified: new Date(), - status: 'Active', - }, function(err, result) { - if (err) { - console.error(err); - - throw err; - } else { - expect(result.userId === 'raymond'); - expect(result.deviceToken === deviceToken); - expect(result.deviceType === 'ios'); - - done(); - } + throw err; + } else { + expect(result.userId === 'raymond'); + expect(result.deviceToken === deviceToken); + expect(result.deviceType === 'ios'); + + done(); + } + } + ); }); - }); - }); + } + ); }); }); From 9112585336f8d17accfdba3902ecaed0c9b22748 Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Wed, 11 Jul 2018 17:01:12 -0400 Subject: [PATCH 27/39] 3.4.0 * fix lint errors (Diana Lau) * chore: update node dependencies (Diana Lau) * [WebFM] cs/pl/ru translation (candytangnb) * chore:update dependencies (Diana Lau) --- CHANGES.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index aa538d3..20ea8e6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,15 @@ +2018-07-11, Version 3.4.0 +========================= + + * fix lint errors (Diana Lau) + + * chore: update node dependencies (Diana Lau) + + * [WebFM] cs/pl/ru translation (candytangnb) + + * chore:update dependencies (Diana Lau) + + 2018-05-08, Version 3.3.1 ========================= diff --git a/package.json b/package.json index d32148b..4b3114b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.3.1", + "version": "3.4.0", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs", From 7cfb459f4b87253f7c6cbc5291c50f1654a5a705 Mon Sep 17 00:00:00 2001 From: jannyHou Date: Mon, 7 Jan 2019 12:09:55 -0500 Subject: [PATCH 28/39] update dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b3114b..e9b77a2 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "chai": "^2.3.0", - "deep-extend": "^0.4.0", + "deep-extend": "^0.5.1", "eslint": "^4.0.0", "eslint-config-loopback": "^10.0.0", "loopback": "^3.0.0", From 034f3d8299bf340425713582bdec4c667fd281fe Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 1 Mar 2019 12:32:47 -0500 Subject: [PATCH 29/39] fix: update lodash --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e9b77a2..a7757eb 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "apn": "^v2.1.2", "async": "^1.5.2", "debug": "^3.1.0", - "lodash": "^4.17.10", + "lodash": "^4.17.11", "mpns": "^2.1.0", "node-cache": "^3.2.1", "node-gcm": "^1.0.0", From 9ddb8c7dd065a166b9de8d6bc2f20b3700cabf08 Mon Sep 17 00:00:00 2001 From: Agnes Lin Date: Tue, 7 May 2019 09:41:24 -0400 Subject: [PATCH 30/39] chore: update copyrights years --- index.js | 2 +- lib/payload.js | 2 +- lib/providers/apns.js | 3 ++- lib/providers/gcm.js | 2 +- lib/providers/index.js | 2 +- lib/push-connector.js | 2 +- lib/push-manager.js | 2 +- models/index.js | 2 +- models/installation.js | 2 +- models/notification.js | 2 +- package.json | 3 ++- test/apns.provider.test.js | 3 ++- test/device-registration.test.js | 2 +- test/gcm.provider.test.js | 2 +- test/helpers/mockery/apns.mockery.js | 3 ++- test/helpers/mockery/gcm.mockery.js | 2 +- test/helpers/mockery/index.js | 2 +- test/helpers/mockery/stub.mockery.js | 2 +- test/helpers/object-mother.js | 2 +- test/helpers/test-data-builder.js | 2 +- test/push-manager.test.js | 2 +- test/push-notification.test.js | 3 ++- 22 files changed, 27 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index 94befd9..b3f7eaa 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/payload.js b/lib/payload.js index 9696826..0870be6 100644 --- a/lib/payload.js +++ b/lib/payload.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/providers/apns.js b/lib/providers/apns.js index 5a7d764..78f443a 100644 --- a/lib/providers/apns.js +++ b/lib/providers/apns.js @@ -1,7 +1,8 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2017. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 + 'use strict'; var g = require('strong-globalize')(); diff --git a/lib/providers/gcm.js b/lib/providers/gcm.js index d74ea00..f0a4388 100644 --- a/lib/providers/gcm.js +++ b/lib/providers/gcm.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/providers/index.js b/lib/providers/index.js index 4406d33..7c447dd 100644 --- a/lib/providers/index.js +++ b/lib/providers/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/push-connector.js b/lib/push-connector.js index b08b564..ddc27a1 100644 --- a/lib/push-connector.js +++ b/lib/push-connector.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/push-manager.js b/lib/push-manager.js index 31f0055..394eda1 100644 --- a/lib/push-manager.js +++ b/lib/push-manager.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/models/index.js b/models/index.js index 64073f0..81f2f37 100644 --- a/models/index.js +++ b/models/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2015. All Rights Reserved. +// Copyright IBM Corp. 2015,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/models/installation.js b/models/installation.js index 3fb80e2..c6c63bf 100644 --- a/models/installation.js +++ b/models/installation.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/models/notification.js b/models/notification.js index 16b7dda..d29cfd1 100644 --- a/models/notification.js +++ b/models/notification.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/package.json b/package.json index a7757eb..ff366c6 100644 --- a/package.json +++ b/package.json @@ -43,5 +43,6 @@ "type": "git", "url": "https://github.com/strongloop/loopback-component-push.git" }, - "license": "Artistic-2.0" + "license": "Artistic-2.0", + "author": "IBM Corp." } diff --git a/test/apns.provider.test.js b/test/apns.provider.test.js index 5973567..2a87c05 100644 --- a/test/apns.provider.test.js +++ b/test/apns.provider.test.js @@ -1,7 +1,8 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 + 'use strict'; var fs = require('fs'); diff --git a/test/device-registration.test.js b/test/device-registration.test.js index c4b483d..c3d550b 100644 --- a/test/device-registration.test.js +++ b/test/device-registration.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/gcm.provider.test.js b/test/gcm.provider.test.js index 4d41519..b7c269e 100644 --- a/test/gcm.provider.test.js +++ b/test/gcm.provider.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/mockery/apns.mockery.js b/test/helpers/mockery/apns.mockery.js index 01ec139..5f7364f 100644 --- a/test/helpers/mockery/apns.mockery.js +++ b/test/helpers/mockery/apns.mockery.js @@ -1,7 +1,8 @@ -// Copyright IBM Corp. 2013. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 + 'use strict'; // This module provides a mocked environment where APN connections diff --git a/test/helpers/mockery/gcm.mockery.js b/test/helpers/mockery/gcm.mockery.js index adab9f8..b41fbf4 100644 --- a/test/helpers/mockery/gcm.mockery.js +++ b/test/helpers/mockery/gcm.mockery.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/mockery/index.js b/test/helpers/mockery/index.js index 6e891b8..a2cd838 100644 --- a/test/helpers/mockery/index.js +++ b/test/helpers/mockery/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/mockery/stub.mockery.js b/test/helpers/mockery/stub.mockery.js index 36932b8..30963a4 100644 --- a/test/helpers/mockery/stub.mockery.js +++ b/test/helpers/mockery/stub.mockery.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/object-mother.js b/test/helpers/object-mother.js index 467cf16..5569e87 100644 --- a/test/helpers/object-mother.js +++ b/test/helpers/object-mother.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2016. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/test-data-builder.js b/test/helpers/test-data-builder.js index 7d7c7e7..1dcf417 100644 --- a/test/helpers/test-data-builder.js +++ b/test/helpers/test-data-builder.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2015. All Rights Reserved. +// Copyright IBM Corp. 2015,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/push-manager.test.js b/test/push-manager.test.js index 95e309c..ee32865 100644 --- a/test/push-manager.test.js +++ b/test/push-manager.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/push-notification.test.js b/test/push-notification.test.js index 4f98a6d..593b21f 100644 --- a/test/push-notification.test.js +++ b/test/push-notification.test.js @@ -1,7 +1,8 @@ -// Copyright IBM Corp. 2013,2015. All Rights Reserved. +// Copyright IBM Corp. 2013,2018. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 + 'use strict'; var expect = require('chai').expect; var loopback = require('loopback'); From 87fffc3d3de65873d3f17a6b1d3c9e33eb35fcfd Mon Sep 17 00:00:00 2001 From: jannyHou Date: Tue, 9 Jul 2019 13:09:06 -0400 Subject: [PATCH 31/39] 3.4.1 * chore: update copyrights years (Agnes Lin) * fix: update lodash (jannyHou) * update dependency (jannyHou) --- CHANGES.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 20ea8e6..c6846fa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,13 @@ +2019-07-09, Version 3.4.1 +========================= + + * chore: update copyrights years (Agnes Lin) + + * fix: update lodash (jannyHou) + + * update dependency (jannyHou) + + 2018-07-11, Version 3.4.0 ========================= diff --git a/package.json b/package.json index ff366c6..11662c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.4.0", + "version": "3.4.1", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs", From bd6769bad5318f294d5a0186777ef3ed1be36fb1 Mon Sep 17 00:00:00 2001 From: Nora Date: Wed, 24 Jul 2019 13:33:35 -0400 Subject: [PATCH 32/39] update eslint dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11662c0..643b2e0 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "devDependencies": { "chai": "^2.3.0", "deep-extend": "^0.5.1", - "eslint": "^4.0.0", + "eslint": "^4.18.2", "eslint-config-loopback": "^10.0.0", "loopback": "^3.0.0", "loopback-connector-mongodb": "^1.8.0", From 7b80d48d506723ffce4b7466ea73ce0763eaf941 Mon Sep 17 00:00:00 2001 From: Nora Date: Wed, 24 Jul 2019 14:36:08 -0400 Subject: [PATCH 33/39] drop support for node.js 6 --- .travis.yml | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ceafed9..437ee5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ language: node_js node_js: - - "6" - "8" - "10" diff --git a/package.json b/package.json index 643b2e0..d38ac23 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "posttest": "npm run lint" }, "engines": { - "node": ">=6" + "node": ">=8" }, "dependencies": { "apn": "^v2.1.2", From 9d479604d4885eba06ed8f2db015ffa4ccfdd122 Mon Sep 17 00:00:00 2001 From: Nora Date: Thu, 25 Jul 2019 09:09:39 -0400 Subject: [PATCH 34/39] update eslint-config-loopback and fix violations --- index.js | 10 +-- lib/payload.js | 29 ++++---- lib/providers/apns.js | 38 +++++----- lib/providers/gcm.js | 30 ++++---- lib/push-connector.js | 14 ++-- lib/push-manager.js | 60 +++++++-------- models/index.js | 10 +-- models/installation.js | 12 +-- models/notification.js | 2 +- package.json | 3 +- test/apns.provider.test.js | 72 +++++++++--------- test/device-registration.test.js | 18 ++--- test/gcm.provider.test.js | 106 ++++++++++++++------------- test/helpers/mockery/apns.mockery.js | 20 ++--- test/helpers/mockery/gcm.mockery.js | 18 ++--- test/helpers/mockery/stub.mockery.js | 18 ++--- test/helpers/object-mother.js | 8 +- test/helpers/test-data-builder.js | 37 +++++----- test/push-manager.test.js | 70 +++++++++--------- test/push-notification.test.js | 18 ++--- 20 files changed, 300 insertions(+), 293 deletions(-) diff --git a/index.js b/index.js index b3f7eaa..87fc797 100644 --- a/index.js +++ b/index.js @@ -5,14 +5,14 @@ 'use strict'; -var SG = require('strong-globalize'); +const SG = require('strong-globalize'); SG.SetRootDir(__dirname); /** * Export the connector */ -var loopback = require('loopback'); -var PushConnector = require('./lib/push-connector'); +const loopback = require('loopback'); +const PushConnector = require('./lib/push-connector'); exports = module.exports = PushConnector; /** @@ -24,7 +24,7 @@ exports.Notification = require('./models').Notification; exports.createPushModel = function(options) { options = options || {}; - var pushDataSource = loopback.createDataSource({ + const pushDataSource = loopback.createDataSource({ connector: PushConnector, installation: options.installation, application: options.application, @@ -33,7 +33,7 @@ exports.createPushModel = function(options) { checkPeriodInSeconds: options.checkPeriodInSeconds, }); - var PushModel = pushDataSource.createModel(options.name || 'Push', {}, + const PushModel = pushDataSource.createModel(options.name || 'Push', {}, {plural: options.plural || 'push'}); return PushModel; }; diff --git a/lib/payload.js b/lib/payload.js index 0870be6..63f721c 100644 --- a/lib/payload.js +++ b/lib/payload.js @@ -5,16 +5,16 @@ 'use strict'; -var g = require('strong-globalize')(); +const g = require('strong-globalize')(); -var serial = 0; -var __hasProp = {}.hasOwnProperty; +let serial = 0; +const __hasProp = {}.hasOwnProperty; // eslint-disable-next-line camelcase Payload.prototype.locale_format = /^[a-z]{2}_[A-Z]{2}$/; function Payload(data) { - var key, prefix, subkey, sum, type, value, _i, _len, _ref, _ref1; + let key, prefix, subkey, sum, type, value, _i, _len, _ref; if (typeof data !== 'object') { throw new Error(g.f('Invalid payload')); } @@ -55,13 +55,12 @@ function Payload(data) { } } sum = 0; - _ref1 = ['title', 'msg', 'data']; + const _ref1 = ['title', 'msg', 'data']; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { type = _ref1[_i]; sum += ((function() { - var _ref2, _results; - _ref2 = this[type]; - _results = []; + const _ref2 = this[type]; + const _results = []; for (key in _ref2) { if (!__hasProp.call(_ref2, key)) continue; _results.push(key); @@ -97,8 +96,8 @@ Payload.prototype.localized = function(type, lang) { }; Payload.prototype.compile = function() { - var lang, msg, type, _i, _len, _ref, _ref1; - _ref = ['title', 'msg']; + let lang, msg, type, _i, _len, _ref1; + const _ref = ['title', 'msg']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { type = _ref[_i]; _ref1 = this[type]; @@ -112,14 +111,14 @@ Payload.prototype.compile = function() { }; Payload.prototype.compileTemplate = function(tmpl) { - var _this = this; + const _this = this; return tmpl.replace(/\$\{(.*?)\}/g, function(match, keyPath) { return _this.variable(keyPath); }); }; Payload.prototype.variable = function(keyPath) { - var key, prefix, _ref, _ref1, _ref2; + let _ref, _ref1; if (keyPath === 'event.name') { if ((_ref = this.event) != null ? _ref.name : undefined) { return (_ref1 = this.event) != null ? _ref1.name : undefined; @@ -127,9 +126,9 @@ Payload.prototype.variable = function(keyPath) { throw new Error(g.f('The ${%s} does not exist', keyPath)); } } - _ref2 = keyPath.split('.', 2); - prefix = _ref2[0]; - key = _ref2[1]; + const _ref2 = keyPath.split('.', 2); + const prefix = _ref2[0]; + const key = _ref2[1]; if (prefix !== 'var' && prefix !== 'data') { throw new Error(g.f('Invalid variable type for ${%s}', keyPath)); } diff --git a/lib/providers/apns.js b/lib/providers/apns.js index 78f443a..7b2c22a 100644 --- a/lib/providers/apns.js +++ b/lib/providers/apns.js @@ -5,13 +5,13 @@ 'use strict'; -var g = require('strong-globalize')(); +const g = require('strong-globalize')(); -var inherits = require('util').inherits; -var assert = require('assert'); -var EventEmitter = require('events').EventEmitter; -var debug = require('debug')('loopback:component:push:provider:apns'); -var apn = require('apn'); +const inherits = require('util').inherits; +const assert = require('assert'); +const EventEmitter = require('events').EventEmitter; +const debug = require('debug')('loopback:component:push:provider:apns'); +const apn = require('apn'); /** * Provider used to distribute push notifications through Apple Push Notification Service. @@ -21,8 +21,8 @@ var apn = require('apn'); function ApnsProvider(pushSettings) { pushSettings = pushSettings || {}; - var settings = pushSettings.apns || {}; - var pushOptions = settings.pushOptions || {}; + const settings = pushSettings.apns || {}; + const pushOptions = settings.pushOptions || {}; // is running sandbox / production if (typeof settings.production === 'undefined') { @@ -32,7 +32,7 @@ function ApnsProvider(pushSettings) { } // validate required properties - var errors = { + const errors = { token: 'JWT Token must be defined, property "token" is undefined.', bundle: 'Bundle should contain the bundle identifier of the app', @@ -41,7 +41,7 @@ function ApnsProvider(pushSettings) { teamId: 'Tokens property "teamId" must be set.', }; - var u = 'undefined'; + const u = 'undefined'; assert.notStrictEqual(typeof settings.token, u, errors.token); assert.notStrictEqual(typeof settings.bundle, u, errors.bundle); @@ -62,7 +62,7 @@ function ApnsProvider(pushSettings) { * @type {null} * @private */ - var _connection = null; + let _connection = null; /** * Sets the stored connection @@ -102,7 +102,7 @@ exports = module.exports = ApnsProvider; * @private */ ApnsProvider.prototype._ensurePushConnection = function(options) { - var self = this; + const self = this; debug('Check whether connected', self.getConnected()); @@ -130,7 +130,7 @@ ApnsProvider.prototype._ensurePushConnection = function(options) { * @param recipient */ function transmissionErrorHandler(code, notification, recipient) { - var err = new Error(g.f('Cannot send {{APNS}} notification: %s', code)); + const err = new Error(g.f('Cannot send {{APNS}} notification: %s', code)); self.emit(err, notification, recipient); } @@ -157,8 +157,8 @@ ApnsProvider.prototype._ensurePushConnection = function(options) { * @param deviceToken */ ApnsProvider.prototype.pushNotification = function(notification, deviceToken) { - var self = this; - var pushOptions = self._pushOptions; + const self = this; + const pushOptions = self._pushOptions; // node-apn has a bug rightnow.. after sending the first // batch of notifications, the connection goes away @@ -167,7 +167,7 @@ ApnsProvider.prototype.pushNotification = function(notification, deviceToken) { // Note parameters are described here: // http://bit.ly/apns-notification-payload - var note = _createNotification(notification, pushOptions); + const note = _createNotification(notification, pushOptions); debug('Pushing notification to %j:', deviceToken, note); @@ -192,7 +192,7 @@ ApnsProvider.prototype.pushNotification = function(notification, deviceToken) { * @private */ function _createNotification(notification, pushOptions) { - var note = new apn.Notification(); + const note = new apn.Notification(); note.expiry = notification.getTimeToLiveInSecondsFromNow() || note.expiry; note.badge = notification.badge; @@ -221,10 +221,10 @@ function _createNotification(notification, pushOptions) { * @private */ function _extractDeviceTokens(failed) { - var tokens = []; + const tokens = []; failed.forEach(function(device) { - var token = device.device; + const token = device.device; tokens.push(token); }); diff --git a/lib/providers/gcm.js b/lib/providers/gcm.js index f0a4388..c87d5f8 100644 --- a/lib/providers/gcm.js +++ b/lib/providers/gcm.js @@ -5,16 +5,16 @@ 'use strict'; -var g = require('strong-globalize')(); +const g = require('strong-globalize')(); -var inherits = require('util').inherits; -var extend = require('util')._extend; -var EventEmitter = require('events').EventEmitter; -var gcm = require('node-gcm'); -var debug = require('debug')('loopback:component:push:provider:gcm'); +const inherits = require('util').inherits; +const extend = require('util')._extend; +const EventEmitter = require('events').EventEmitter; +const gcm = require('node-gcm'); +const debug = require('debug')('loopback:component:push:provider:gcm'); function GcmProvider(pushSettings) { - var settings = pushSettings.gcm || {}; + const settings = pushSettings.gcm || {}; this._setupPushConnection(settings); } @@ -28,18 +28,18 @@ GcmProvider.prototype._setupPushConnection = function(options) { }; GcmProvider.prototype.pushNotification = function(notification, deviceToken) { - var self = this; + const self = this; - var registrationIds = (typeof deviceToken == 'string') ? + const registrationIds = (typeof deviceToken == 'string') ? [deviceToken] : deviceToken; - var message = this._createMessage(notification); + const message = this._createMessage(notification); debug('Sending message to %j: %j', registrationIds, message); this._connection.send(message, registrationIds, 3, function(err, result) { if (!err && result && result.failure) { - var devicesGoneRegistrationIds = []; - var errors = []; - var code; + const devicesGoneRegistrationIds = []; + const errors = []; + let code; result.results.forEach(function(value, index) { code = value && value.error; if (code === 'NotRegistered' || code === 'InvalidRegistration') { @@ -73,13 +73,13 @@ GcmProvider.prototype.pushNotification = function(notification, deviceToken) { GcmProvider.prototype._createMessage = function(notification) { // Message parameters are documented here: // https://developers.google.com/cloud-messaging/server-ref - var message = new gcm.Message({ + const message = new gcm.Message({ timeToLive: notification.getTimeToLiveInSecondsFromNow(), collapseKey: notification.collapseKey, delayWhileIdle: notification.delayWhileIdle, }); - var propNames = Object.keys(notification); + const propNames = Object.keys(notification); // GCM does not have reserved message parameters for alert or badge, adding them as data. propNames.push('alert', 'badge'); diff --git a/lib/push-connector.js b/lib/push-connector.js index ddc27a1..4773e35 100644 --- a/lib/push-connector.js +++ b/lib/push-connector.js @@ -5,27 +5,27 @@ 'use strict'; -var loopback = require('loopback'); -var PushManager = require('./push-manager'); +const loopback = require('loopback'); +const PushManager = require('./push-manager'); /** * Export the initialize method to Loopback DataSource * @param {Object} dataSource Loopback dataSource (Memory, etc). * @param {Function} callback (unused) */ exports.initialize = function(dataSource, callback) { - var settings = dataSource.settings || {}; + const settings = dataSource.settings || {}; // Create an instance of the APNSManager - var connector = new PushManager(settings); + const connector = new PushManager(settings); dataSource.connector = connector; dataSource.connector.dataSource = dataSource; connector.DataAccessObject = function() {}; - for (var m in PushManager.prototype) { - var method = PushManager.prototype[m]; + for (const m in PushManager.prototype) { + const method = PushManager.prototype[m]; if ('function' === typeof method) { connector.DataAccessObject[m] = method.bind(connector); - for (var k in method) { + for (const k in method) { connector.DataAccessObject[m][k] = method[k]; } } diff --git a/lib/push-manager.js b/lib/push-manager.js index 394eda1..ad733d8 100644 --- a/lib/push-manager.js +++ b/lib/push-manager.js @@ -5,20 +5,20 @@ 'use strict'; -var g = require('strong-globalize')(); +const g = require('strong-globalize')(); -var assert = require('assert'); -var inherits = require('util').inherits; -var EventEmitter = require('events').EventEmitter; -var format = require('util').format; -var async = require('async'); -var providers = require('./providers'); -var loopback = require('loopback'); -var NodeCache = require('node-cache'); -var debug = require('debug')('loopback:component:push:push-manager'); +const assert = require('assert'); +const inherits = require('util').inherits; +const EventEmitter = require('events').EventEmitter; +const format = require('util').format; +const async = require('async'); +const providers = require('./providers'); +const loopback = require('loopback'); +const NodeCache = require('node-cache'); +const debug = require('debug')('loopback:component:push:push-manager'); -var Installation = require('../models').Installation; -var Notification = require('../models').Notification; +const Installation = require('../models').Installation; +const Notification = require('../models').Notification; /*! * Exports a function to bootstrap PushManager @@ -128,12 +128,12 @@ PushManager.providers = { * matching the deviceType (android, ios) */ PushManager.prototype.configureProvider = function(deviceType, pushSettings) { - var Provider = PushManager.providers[deviceType]; + const Provider = PushManager.providers[deviceType]; if (!Provider) { return null; } - var provider = new Provider(pushSettings); + const provider = new Provider(pushSettings); provider.on('devicesGone', function(deviceTokens) { this.Installation.destroyAll({ deviceType: deviceType, @@ -156,11 +156,11 @@ PushManager.prototype.configureProvider = function(deviceType, pushSettings) { */ PushManager.prototype.configureApplication = function(appId, deviceType, cb) { assert.ok(cb, 'callback should be defined'); - var self = this; - var msg; + const self = this; + let msg; // Check the cache first - var cacheApp = self.applicationsCache.get(appId); + let cacheApp = self.applicationsCache.get(appId); if (cacheApp && cacheApp[deviceType]) { return process.nextTick(function() { cb(null, cacheApp[deviceType]); @@ -178,7 +178,8 @@ PushManager.prototype.configureApplication = function(appId, deviceType, cb) { if (!application) { msg = format( 'Cannot configure push notifications - unknown application id %j', - appId); + appId + ); debug('Error: %s', msg); err = new Error(msg); @@ -186,11 +187,12 @@ PushManager.prototype.configureApplication = function(appId, deviceType, cb) { return cb(err); } - var pushSettings = application.pushSettings; + const pushSettings = application.pushSettings; if (!pushSettings) { msg = format( 'No push settings configured for application %j (id: %j)', - application.name, application.id); + application.name, application.id + ); debug('Error: %s', msg); err = new Error(msg); @@ -209,7 +211,7 @@ PushManager.prototype.configureApplication = function(appId, deviceType, cb) { deviceType ); - var provider = self.configureProvider(deviceType, pushSettings); + const provider = self.configureProvider(deviceType, pushSettings); if (!provider) { msg = 'There is no provider registered for deviceType ' + deviceType; @@ -236,11 +238,11 @@ PushManager.prototype.configureApplication = function(appId, deviceType, cb) { */ PushManager.prototype.notifyById = function(installationId, notification, cb) { assert.ok(cb, 'callback should be defined'); - var self = this; + const self = this; self.Installation.findById(installationId, function(err, installation) { if (err) return cb(err); if (!installation) { - var msg = 'Installation id ' + installationId + ' not found'; + const msg = 'Installation id ' + installationId + ' not found'; debug('notifyById failed: ' + msg); err = new Error(msg); err.details = {installationId: installationId}; @@ -260,8 +262,8 @@ PushManager.prototype.notifyById = function(installationId, notification, cb) { PushManager.prototype.notifyByQuery = function(installationQuery, notification, cb) { assert.ok(cb, 'callback should be defined'); - var self = this; - var filter = {where: installationQuery}; + const self = this; + const filter = {where: installationQuery}; self.Installation.find(filter, function(err, installationList) { if (err) return cb(err); async.each( @@ -288,9 +290,9 @@ PushManager.prototype.notify = function(installation, notification, cb) { return cb(new Error(g.f('notification must be an object'))); } - var appId = installation.appId; - var deviceToken = installation.deviceToken; - var deviceType = installation.deviceType; + const appId = installation.appId; + const deviceToken = installation.deviceToken; + const deviceType = installation.deviceType; // Normalize the notification from a plain object // for remote calls @@ -372,7 +374,7 @@ PushManager.prototype.notifyMany = function(appId, deviceType, deviceTokens, */ function setRemoting(fn, options) { options = options || {}; - for (var opt in options) { + for (const opt in options) { if (options.hasOwnProperty(opt)) { fn[opt] = options[opt]; } diff --git a/models/index.js b/models/index.js index 81f2f37..69851a5 100644 --- a/models/index.js +++ b/models/index.js @@ -7,16 +7,16 @@ // mostly borrowed from // https://github.com/strongloop/loopback-component-passport/blob/master/lib/index.js -var loopback = require('loopback'); -var DataModel = loopback.PersistedModel || loopback.DataModel; +const loopback = require('loopback'); +const DataModel = loopback.PersistedModel || loopback.DataModel; function loadModel(jsonFile) { - var modelDefinition = require(jsonFile); + const modelDefinition = require(jsonFile); return DataModel.extend(modelDefinition.name, modelDefinition.properties); } -var InstallationModel = loadModel('./installation.json'); -var NotificationModel = loadModel('./notification.json'); +const InstallationModel = loadModel('./installation.json'); +const NotificationModel = loadModel('./notification.json'); /** * Export two model classes as properties diff --git a/models/installation.js b/models/installation.js index c6c63bf..8d4ec47 100644 --- a/models/installation.js +++ b/models/installation.js @@ -5,7 +5,7 @@ 'use strict'; -var _ = require('lodash'); +const _ = require('lodash'); /** * Installation Model connects a mobile application to a device, the user and @@ -31,7 +31,7 @@ var _ = require('lodash'); */ module.exports = function(Installation) { Installation.observe('before save', function trip(ctx, next) { - var install = ctx.instance || ctx.data; + const install = ctx.instance || ctx.data; install.modified = new Date(); next(); }); @@ -48,7 +48,7 @@ module.exports = function(Installation) { cb = appVersion; appVersion = undefined; } - var filter = {where: { + const filter = {where: { appId: appId, appVersion: appVersion, deviceType: deviceType}, @@ -63,7 +63,7 @@ module.exports = function(Installation) { * @param {function(Error=,Installation[])} cb Callback function passed to find() with `cb(err, obj[])` signature. */ Installation.findByUser = function(deviceType, userId, cb) { - var filter = {where: {userId: userId, deviceType: deviceType}}; + const filter = {where: {userId: userId, deviceType: deviceType}}; this.find(filter, cb); }; @@ -77,7 +77,7 @@ module.exports = function(Installation) { if (typeof subscriptions === 'string') { subscriptions = subscriptions.split(/[\s,]+/); } - var filter = {where: { + const filter = {where: { subscriptions: {inq: subscriptions}, deviceType: deviceType, }, @@ -99,7 +99,7 @@ module.exports = function(Installation) { fn.shared = true; } - var aDefs = {type: 'string', http: {source: 'query'}}; + const aDefs = {type: 'string', http: {source: 'query'}}; setRemoting(Installation.findByApp, { description: 'Find installations by application id', diff --git a/models/notification.js b/models/notification.js index d29cfd1..d2a57b1 100644 --- a/models/notification.js +++ b/models/notification.js @@ -45,7 +45,7 @@ module.exports = function(Notification) { Notification.hideInternalProperties = true; Notification.observe('before save', function trip(ctx, next) { - var notification = ctx.instance || ctx.data; + const notification = ctx.instance || ctx.data; notification.modified = notification.scheduledTime = new Date(); next(); }); diff --git a/package.json b/package.json index d38ac23..c1d4e76 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "main": "index.js", "scripts": { "lint": "eslint .", + "lint:fix": "eslint . --fix", "test": "mocha", "posttest": "npm run lint" }, @@ -32,7 +33,7 @@ "chai": "^2.3.0", "deep-extend": "^0.5.1", "eslint": "^4.18.2", - "eslint-config-loopback": "^10.0.0", + "eslint-config-loopback": "^13.1.0", "loopback": "^3.0.0", "loopback-connector-mongodb": "^1.8.0", "mocha": "^4.0.0", diff --git a/test/apns.provider.test.js b/test/apns.provider.test.js index 2a87c05..2dd910a 100644 --- a/test/apns.provider.test.js +++ b/test/apns.provider.test.js @@ -5,18 +5,18 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var expect = require('chai').expect; -var assert = require('assert'); -var sinon = require('sinon'); -var loopback = require('loopback'); -var ApnsProvider = require('../lib/providers/apns'); -var mockery = require('./helpers/mockery').apns; -var objectMother = require('./helpers/object-mother'); - -var aDeviceToken = 'a-device-token'; -var defaultConfiguration = { +const fs = require('fs'); +const path = require('path'); +const expect = require('chai').expect; +const assert = require('assert'); +const sinon = require('sinon'); +const loopback = require('loopback'); +const ApnsProvider = require('../lib/providers/apns'); +const mockery = require('./helpers/mockery').apns; +const objectMother = require('./helpers/object-mother'); + +const aDeviceToken = 'a-device-token'; +const defaultConfiguration = { apns: { token: { keyId: 'key_id', @@ -27,22 +27,22 @@ var defaultConfiguration = { }, }; -var ds = loopback.createDataSource('db', { +const ds = loopback.createDataSource('db', { connector: loopback.Memory, }); -var Application = loopback.Application; +const Application = loopback.Application; Application.attachTo(ds); -var PushConnector = require('../'); -var Installation = PushConnector.Installation; +const PushConnector = require('../'); +const Installation = PushConnector.Installation; Installation.attachTo(ds); -var Notification = PushConnector.Notification; +const Notification = PushConnector.Notification; Notification.attachTo(ds); describe('APNS provider', function() { - var provider; + let provider; describe('in sandbox', function() { beforeEach(mockery.setUp); @@ -54,14 +54,14 @@ describe('APNS provider', function() { it('sends Notification as an APN message', function(done) { givenProviderWithConfig(); - var notification = aNotification({ + const notification = aNotification({ aKey: 'a-value', }); provider.pushNotification(notification, aDeviceToken); - var apnArgs = mockery.firstPushNotificationArgs(); + const apnArgs = mockery.firstPushNotificationArgs(); - var note = apnArgs[0]; + const note = apnArgs[0]; expect(note.expiry, 'expiry').to.equal(0); expect(note.alert, 'alert').to.equal(undefined); expect(note.badge, 'badge').to.equal(undefined); @@ -77,7 +77,7 @@ describe('APNS provider', function() { it('passes through special APN parameters', function(done) { givenProviderWithConfig(); - var notification = aNotification({ + const notification = aNotification({ alert: 'You have a message from StrongLoop', messageFrom: 'StrongLoop', contentAvailable: true, @@ -87,10 +87,10 @@ describe('APNS provider', function() { }); provider.pushNotification(notification, aDeviceToken); - var apnArgs = mockery.firstPushNotificationArgs(); + const apnArgs = mockery.firstPushNotificationArgs(); - var note = apnArgs[0]; - var payload = note.toJSON(); + const note = apnArgs[0]; + const payload = note.toJSON(); expect( payload.aps['content-available'], 'aps.content-available' @@ -109,11 +109,11 @@ describe('APNS provider', function() { it('raises "devicesGone" event when feedback arrives', function(done) { givenProviderWithConfig(); - var notification = aNotification({ + const notification = aNotification({ aKey: 'a-value', }); - var eventSpy = sinon.spy(); + const eventSpy = sinon.spy(); provider.on('devicesGone', eventSpy); provider.pushNotification(notification, aDeviceToken); @@ -135,37 +135,37 @@ describe('APNS provider', function() { it('converts expirationInterval to APNS expiry', function() { givenProviderWithConfig(); - var notification = aNotification({ + const notification = aNotification({ expirationInterval: 1, /* second */ }); provider.pushNotification(notification, aDeviceToken); - var note = mockery.firstPushNotificationArgs()[0]; + const note = mockery.firstPushNotificationArgs()[0]; expect(note.expiry).to.equal(1); }); it('converts expirationTime to APNS expiry relative to now', function() { givenProviderWithConfig(); - var notification = aNotification({ + const notification = aNotification({ expirationTime: new Date(this.clock.now + 1000 /* 1 second */), }); provider.pushNotification(notification, aDeviceToken); - var note = mockery.firstPushNotificationArgs()[0]; + const note = mockery.firstPushNotificationArgs()[0]; expect(note.expiry).to.equal(1); }); it('ignores Notification properties not applicable', function() { givenProviderWithConfig(); - var notification = aNotification( + const notification = aNotification( objectMother.allNotificationProperties() ); provider.pushNotification(notification, aDeviceToken); - var note = mockery.firstPushNotificationArgs()[0]; + const note = mockery.firstPushNotificationArgs()[0]; expect(note.payload).to.eql({}); }); }); @@ -247,7 +247,7 @@ describe('APNS provider', function() { }); it('reports error when bundle is not specified', function(done) { - var test = function() { + const test = function() { givenProviderWithConfig({ apns: { token: { @@ -264,7 +264,7 @@ describe('APNS provider', function() { }); it('reports error when token is not specified', function(done) { - var test = function() { + const test = function() { givenProviderWithConfig({ bundle: 'the_bundle', }); @@ -275,7 +275,7 @@ describe('APNS provider', function() { }); it('reports error when token is missing a property', function(done) { - var test = function() { + const test = function() { givenProviderWithConfig({ token: { keyId: 'key_id', diff --git a/test/device-registration.test.js b/test/device-registration.test.js index c3d550b..b01f590 100644 --- a/test/device-registration.test.js +++ b/test/device-registration.test.js @@ -4,21 +4,21 @@ // License text available at https://opensource.org/licenses/Artistic-2.0 'use strict'; -var assert = require('assert'); -var loopback = require('loopback'); +const assert = require('assert'); +const loopback = require('loopback'); -var ds = loopback.createDataSource('db', { +const ds = loopback.createDataSource('db', { connector: loopback.Memory, }); -var PushConnector = require('../'); -var Installation = PushConnector.Installation; +const PushConnector = require('../'); +const Installation = PushConnector.Installation; Installation.attachTo(ds); describe('Installation', function() { - var registration = null; + let registration = null; it('registers a new installation', function(done) { - var token = + const token = '75624450 3c9f95b4 9d7ff821 20dc193c a1e3a7cb 56f60c2e ' + 'f2a19241 e8f33305'; Installation.create( @@ -38,7 +38,7 @@ describe('Installation', function() { done(err, result); return; } else { - var reg = result; + const reg = result; assert.equal(reg.appId, 'MyLoopbackApp'); assert.equal(reg.userId, 'raymond'); assert.equal(reg.deviceType, 'ios'); @@ -55,7 +55,7 @@ describe('Installation', function() { ) { assert(!err); assert.equal(results.length, 1); - var reg = results[0]; + const reg = results[0]; assert.equal(reg.appId, 'MyLoopbackApp'); assert.equal(reg.userId, 'raymond'); assert.equal(reg.deviceType, 'ios'); diff --git a/test/gcm.provider.test.js b/test/gcm.provider.test.js index b7c269e..d6671d7 100644 --- a/test/gcm.provider.test.js +++ b/test/gcm.provider.test.js @@ -5,16 +5,16 @@ 'use strict'; -var expect = require('chai').expect; -var sinon = require('sinon'); -var extend = require('util')._extend; -var GcmProvider = require('../lib/providers/gcm'); -var mockery = require('./helpers/mockery').gcm; -var objectMother = require('./helpers/object-mother'); -var loopback = require('loopback'); - -var aDeviceToken = 'a-device-token'; -var aDeviceTokenList = [ +const expect = require('chai').expect; +const sinon = require('sinon'); +const extend = require('util')._extend; +const GcmProvider = require('../lib/providers/gcm'); +const mockery = require('./helpers/mockery').gcm; +const objectMother = require('./helpers/object-mother'); +const loopback = require('loopback'); + +const aDeviceToken = 'a-device-token'; +const aDeviceTokenList = [ 'first-device-token', 'second-device-token', 'third-device-token', @@ -22,22 +22,22 @@ var aDeviceTokenList = [ 'fifth-device-token', ]; -var ds = loopback.createDataSource('db', { +const ds = loopback.createDataSource('db', { connector: loopback.Memory, }); -var Application = loopback.Application; +const Application = loopback.Application; Application.attachTo(ds); -var PushConnector = require('../'); -var Installation = PushConnector.Installation; +const PushConnector = require('../'); +const Installation = PushConnector.Installation; Installation.attachTo(ds); -var Notification = PushConnector.Notification; +const Notification = PushConnector.Notification; Notification.attachTo(ds); describe('GCM provider', function() { - var provider; + let provider; beforeEach(mockery.setUp); beforeEach(setUpFakeTimers); @@ -50,14 +50,14 @@ describe('GCM provider', function() { describe('for single device token', function() { it('sends Notification as a GCM message', function(done) { - var notification = aNotification({aKey: 'a-value'}); + const notification = aNotification({aKey: 'a-value'}); notification.alert = 'alert message'; notification.badge = 1; provider.pushNotification(notification, aDeviceToken); - var gcmArgs = mockery.firstPushNotificationArgs(); + const gcmArgs = mockery.firstPushNotificationArgs(); - var msg = gcmArgs[0]; + const msg = gcmArgs[0]; expect(msg.params.collapseKey, 'collapseKey').to.equal(undefined); expect(msg.params.delayWhileIdle, 'delayWhileIdle').to.equal(undefined); expect(msg.params.timeToLive, 'timeToLive').to.equal(undefined); @@ -72,10 +72,10 @@ describe('GCM provider', function() { }); it('emits "error" when GCM send fails', function() { - var anError = new Error('test-error'); + const anError = new Error('test-error'); mockery.givenPushNotificationFailsWith(anError); - var eventSpy = spyOnProviderError(); + const eventSpy = spyOnProviderError(); provider.pushNotification(aNotification(), aDeviceToken); @@ -87,11 +87,11 @@ describe('GCM provider', function() { it('emits "error" event when GCM returns error result', function() { // This is a real result returned by GCM - var errorResult = aGcmResult([{error: 'MismatchSenderId'}]); + const errorResult = aGcmResult([{error: 'MismatchSenderId'}]); mockery.pushNotificationCallbackArgs = [null, errorResult]; - var eventSpy = spyOnProviderError(); + const eventSpy = spyOnProviderError(); provider.pushNotification(aNotification(), aDeviceToken); @@ -102,11 +102,11 @@ describe('GCM provider', function() { }); it('emits "devicesGone" when GCM returns NotRegistered', function(done) { - var errorResult = aGcmResult([{error: 'NotRegistered'}]); + const errorResult = aGcmResult([{error: 'NotRegistered'}]); mockery.pushNotificationCallbackArgs = [null, errorResult]; - var eventSpy = sinon.spy(); + const eventSpy = sinon.spy(); provider.on('devicesGone', eventSpy); provider.on('error', function(err) { throw err; @@ -114,7 +114,7 @@ describe('GCM provider', function() { provider.pushNotification(aNotification(), aDeviceToken); - var expectedIds = [aDeviceToken]; + const expectedIds = [aDeviceToken]; expect(eventSpy.args[0]).to.deep.equal([expectedIds]); done(); }); @@ -122,12 +122,12 @@ describe('GCM provider', function() { describe('for multiple device tokens', function() { it('sends Notification as a GCM message', function(done) { - var notification = aNotification({aKey: 'a-value'}); + const notification = aNotification({aKey: 'a-value'}); provider.pushNotification(notification, aDeviceTokenList); - var gcmArgs = mockery.pushNotification.args[0]; + const gcmArgs = mockery.pushNotification.args[0]; - var msg = gcmArgs[0]; + const msg = gcmArgs[0]; expect(msg.params.collapseKey, 'collapseKey').to.equal(undefined); expect(msg.params.delayWhileIdle, 'delayWhileIdle').to.equal(undefined); expect(msg.params.timeToLive, 'timeToLive').to.equal(undefined); @@ -138,13 +138,13 @@ describe('GCM provider', function() { }); it('handles GCM response for multiple device tokens', function(done) { - var gcmError = new Error( + const gcmError = new Error( 'GCM error code: MismatchSenderId, ' + 'deviceToken: third-device-token\nGCM error code: ' + 'MismatchSenderId, deviceToken: fifth-device-token' ); - var gcmResult = aGcmResult([ + const gcmResult = aGcmResult([ {error: 'InvalidRegistration'}, // eslint-disable-next-line { message_id: '1234567890' }, @@ -155,7 +155,7 @@ describe('GCM provider', function() { mockery.pushNotificationCallbackArgs = [null, gcmResult]; - var eventSpy = sinon.spy(); + const eventSpy = sinon.spy(); provider.on('devicesGone', eventSpy); provider.on('error', function(err) { expect(err.message).to.equal(gcmError.message); @@ -163,7 +163,7 @@ describe('GCM provider', function() { provider.pushNotification(aNotification(), aDeviceTokenList); - var expectedIds = [aDeviceTokenList[0], aDeviceTokenList[3]]; + const expectedIds = [aDeviceTokenList[0], aDeviceTokenList[3]]; expect(eventSpy.calledOnce, 'error should be emitted once').to.equal( true ); @@ -173,38 +173,38 @@ describe('GCM provider', function() { }); it('converts expirationInterval to GCM timeToLive', function() { - var notification = aNotification({expirationInterval: 1}); + const notification = aNotification({expirationInterval: 1}); provider.pushNotification(notification, aDeviceToken); - var message = mockery.firstPushNotificationArgs()[0]; + const message = mockery.firstPushNotificationArgs()[0]; expect(message.params.timeToLive).to.equal(1); }); it('converts expirationTime to GCM timeToLive relative to now', function() { - var notification = aNotification({ + const notification = aNotification({ expirationTime: new Date(this.clock.now + 1000 /* 1 second */), }); provider.pushNotification(notification, aDeviceToken); - var message = mockery.firstPushNotificationArgs()[0]; + const message = mockery.firstPushNotificationArgs()[0]; expect(message.params.timeToLive).to.equal(1); }); it('forwards android parameters', function() { - var notification = aNotification({ + const notification = aNotification({ collapseKey: 'a-collapse-key', delayWhileIdle: true, }); provider.pushNotification(notification, aDeviceToken); - var message = mockery.firstPushNotificationArgs()[0]; + const message = mockery.firstPushNotificationArgs()[0]; expect(message.params.collapseKey).to.equal('a-collapse-key'); expect(message.params.delayWhileIdle, 'delayWhileIdle').to.equal(true); }); it('adds appropriate fcm properties to the notification', function() { - var note = { + const note = { messageFrom: 'StrongLoop', alert: 'Hello from StrongLoop', icon: 'logo.png', @@ -215,10 +215,10 @@ describe('GCM provider', function() { // eslint-disable-next-line click_action: 'OPEN_ACTIVITY_1', }; - var notification = aNotification(note); + const notification = aNotification(note); provider.pushNotification(notification, aDeviceToken); - var message = mockery.firstPushNotificationArgs()[0]; + const message = mockery.firstPushNotificationArgs()[0]; expect(message.params.notification).to.eql({ title: note.messageFrom, body: note.alert, @@ -233,10 +233,12 @@ describe('GCM provider', function() { }); it('ignores Notification properties not applicable', function() { - var notification = aNotification(objectMother.allNotificationProperties()); + const notification = aNotification( + objectMother.allNotificationProperties() + ); provider.pushNotification(notification, aDeviceToken); - var message = mockery.firstPushNotificationArgs()[0]; + const message = mockery.firstPushNotificationArgs()[0]; expect(message.params.data).to.deep.equal({ alert: 'an-alert', badge: 1230001, @@ -244,7 +246,7 @@ describe('GCM provider', function() { }); it('ignores Notification properties null or undefined', function() { - var notification = aNotification({ + const notification = aNotification({ aFalse: false, aTrue: true, aNull: null, @@ -252,12 +254,12 @@ describe('GCM provider', function() { }); provider.pushNotification(notification, aDeviceToken); - var message = mockery.firstPushNotificationArgs()[0]; + const message = mockery.firstPushNotificationArgs()[0]; expect(message.params.data).to.deep.equal({aFalse: false, aTrue: true}); }); it('supports data-only notifications', function() { - var note = { + const note = { messageFrom: 'StrongLoop', alert: 'Hello from StrongLoop', icon: 'logo.png', @@ -265,10 +267,10 @@ describe('GCM provider', function() { badge: 5, dataOnly: true, }; - var notification = aNotification(note); + const notification = aNotification(note); provider.pushNotification(notification, aDeviceToken); - var message = mockery.firstPushNotificationArgs()[0]; + const message = mockery.firstPushNotificationArgs()[0]; expect(message.params.data).to.eql({ messageFrom: 'StrongLoop', alert: 'Hello from StrongLoop', @@ -297,11 +299,11 @@ describe('GCM provider', function() { } function aGcmResult(results) { - var success = results.filter(function(item) { + const success = results.filter(function(item) { return item.message_id; }).length; - var failure = results.filter(function(item) { + const failure = results.filter(function(item) { return item.error; }).length; @@ -325,7 +327,7 @@ describe('GCM provider', function() { } function spyOnProviderError() { - var eventSpy = sinon.spy(); + const eventSpy = sinon.spy(); provider.on('error', eventSpy); return eventSpy; } diff --git a/test/helpers/mockery/apns.mockery.js b/test/helpers/mockery/apns.mockery.js index 5f7364f..f965299 100644 --- a/test/helpers/mockery/apns.mockery.js +++ b/test/helpers/mockery/apns.mockery.js @@ -9,11 +9,11 @@ // are calling callbacks provided by tests instead of communicating with // the real service -var EventEmitter = require('events').EventEmitter; -var apn = require('apn'); -var sinon = require('sinon'); +const EventEmitter = require('events').EventEmitter; +const apn = require('apn'); +const sinon = require('sinon'); -var mockery = exports; +const mockery = exports; /** * The options passed to `apn.Connection` constructor. @@ -51,14 +51,14 @@ mockery.firstPushNotificationArgs = function() { return mockery.send.firstCall.args; }; -var apnsSnapshot = {}; -var defaultExports = {}; +const apnsSnapshot = {}; +const defaultExports = {}; /** * Setup the mockery. This method should be called before each test. */ exports.setUp = function() { - var key; + let key; for (key in apn) { apnsSnapshot[key] = apn[key]; } @@ -67,7 +67,7 @@ exports.setUp = function() { defaultExports[key] = exports[key]; } - var expectedResponse = { + const expectedResponse = { failed: [ { device: 'some_failing_device_token', @@ -82,7 +82,7 @@ exports.setUp = function() { apn.Provider = apn.provider = function(opts) { mockery.connectionOptions = opts; - var conn = new EventEmitter(); + const conn = new EventEmitter(); conn.send = mockery.send; return conn; @@ -94,7 +94,7 @@ exports.setUp = function() { * This method should be called after each test. */ exports.tearDown = function() { - var key; + let key; for (key in apnsSnapshot) { apn[key] = apnsSnapshot[key]; diff --git a/test/helpers/mockery/gcm.mockery.js b/test/helpers/mockery/gcm.mockery.js index b41fbf4..67d621d 100644 --- a/test/helpers/mockery/gcm.mockery.js +++ b/test/helpers/mockery/gcm.mockery.js @@ -9,11 +9,11 @@ // are calling callbacks provided by tests instead of communicating with // the real service -var EventEmitter = require('events').EventEmitter; -var gcm = require('node-gcm'); -var sinon = require('sinon'); +const EventEmitter = require('events').EventEmitter; +const gcm = require('node-gcm'); +const sinon = require('sinon'); -var mockery = exports; +const mockery = exports; /** * The options passed to `gcm.Sender` constructor. @@ -50,14 +50,14 @@ mockery.givenPushNotificationFailsWith = function(err) { mockery.pushNotificationCallbackArgs = [err]; }; -var gcmSnapshot = {}; -var defaultExports = {}; +const gcmSnapshot = {}; +const defaultExports = {}; /** * Setup the mockery. This method should be called before each test. */ exports.setUp = function() { - var key; + let key; for (key in gcm) { gcmSnapshot[key] = gcm[key]; } @@ -71,7 +71,7 @@ exports.setUp = function() { gcm.Sender = function(opts) { mockery.senderOptions = Array.prototype.slice.call(arguments); - var sender = {}; + const sender = {}; sender.send = function(message, registrationId, retries, callback) { mockery.pushNotification.apply(this, arguments); callback.apply(null, mockery.pushNotificationCallbackArgs); @@ -85,7 +85,7 @@ exports.setUp = function() { * This method should be called after each test. */ exports.tearDown = function() { - var key; + let key; for (key in gcmSnapshot) { gcm[key] = gcmSnapshot[key]; diff --git a/test/helpers/mockery/stub.mockery.js b/test/helpers/mockery/stub.mockery.js index 30963a4..94e3d56 100644 --- a/test/helpers/mockery/stub.mockery.js +++ b/test/helpers/mockery/stub.mockery.js @@ -8,13 +8,13 @@ // This module provides a mocked environment with a special "stub" // provider that does not depend on any real implementation (GCM, APNS) -var inherits = require('util').inherits; -var EventEmitter = require('events').EventEmitter; -var expect = require('chai').expect; -var sinon = require('sinon'); -var PushManager = require('../../../lib/push-manager'); +const inherits = require('util').inherits; +const EventEmitter = require('events').EventEmitter; +const expect = require('chai').expect; +const sinon = require('sinon'); +const PushManager = require('../../../lib/push-manager'); -var mockery = exports; +const mockery = exports; /** * The device type used for registration with PushManager. @@ -79,13 +79,13 @@ StubProvider.prototype.emitDevicesGone = function(devices) { this.emit('devicesGone', devices); }; -var defaultExports = {}; +const defaultExports = {}; /** * Setup the mockery. This method should be called before each test. */ exports.setUp = function() { - for (var key in exports) { + for (const key in exports) { defaultExports[key] = exports[key]; } @@ -98,7 +98,7 @@ exports.setUp = function() { * This method should be called after each test. */ exports.tearDown = function() { - for (var key in defaultExports) { + for (const key in defaultExports) { exports[key] = defaultExports[key]; } diff --git a/test/helpers/object-mother.js b/test/helpers/object-mother.js index 5569e87..dbe41fb 100644 --- a/test/helpers/object-mother.js +++ b/test/helpers/object-mother.js @@ -35,10 +35,10 @@ exports.apnsDevKey = function() { return readCredentialsSync('apns_key_dev.pem'); }; -var path = require('path'); -var fs = require('fs'); +const path = require('path'); +const fs = require('fs'); function readCredentialsSync(fileName) { - var relativePath = '../fixtures'; - var credentialsDir = path.resolve(__dirname, relativePath); + const relativePath = '../fixtures'; + const credentialsDir = path.resolve(__dirname, relativePath); return fs.readFileSync(path.join(credentialsDir, fileName), 'UTF-8'); } diff --git a/test/helpers/test-data-builder.js b/test/helpers/test-data-builder.js index 1dcf417..f4ce327 100644 --- a/test/helpers/test-data-builder.js +++ b/test/helpers/test-data-builder.js @@ -4,8 +4,8 @@ // License text available at https://opensource.org/licenses/Artistic-2.0 'use strict'; -var extend = require('util')._extend; -var async = require('async'); +const extend = require('util')._extend; +const async = require('async'); module.exports = exports = TestDataBuilder; @@ -82,13 +82,14 @@ TestDataBuilder.prototype.buildTo = function(context, callback) { async.eachSeries( this._definitions, this._buildObject.bind(this), - callback); + callback + ); }; TestDataBuilder.prototype._buildObject = function(definition, callback) { - var defaultValues = this._gatherDefaultPropertyValues(definition.model); - var values = extend(defaultValues, definition.properties || {}); - var resolvedValues = this._resolveValues(values); + const defaultValues = this._gatherDefaultPropertyValues(definition.model); + const values = extend(defaultValues, definition.properties || {}); + const resolvedValues = this._resolveValues(values); definition.model.create(resolvedValues, function(err, result) { if (err) { @@ -96,7 +97,8 @@ TestDataBuilder.prototype._buildObject = function(definition, callback) { 'Cannot build object %j - %s\nDetails: %j', definition, err.message, - err.details); + err.details + ); } else { this._context[definition.name] = result; } @@ -106,9 +108,9 @@ TestDataBuilder.prototype._buildObject = function(definition, callback) { }; TestDataBuilder.prototype._resolveValues = function(values) { - var result = {}; - for (var key in values) { - var val = values[key]; + const result = {}; + for (const key in values) { + let val = values[key]; if (val instanceof Reference) { val = values[key].resolveFromContext(this._context); } @@ -117,23 +119,24 @@ TestDataBuilder.prototype._resolveValues = function(values) { return result; }; -var valueCounter = 0; +let valueCounter = 0; TestDataBuilder.prototype._gatherDefaultPropertyValues = function(Model) { - var result = {}; + const result = {}; Model.forEachProperty(function createDefaultPropertyValue(name) { - var prop = Model.definition.properties[name]; + const prop = Model.definition.properties[name]; if (!prop.required) return; switch (prop.type) { case String: - var generatedString = 'a test ' + name + ' #' + (++valueCounter); + let generatedString = 'a test ' + name + ' #' + (++valueCounter); // If this property has a maximum length, ensure that the generated // string is not longer than the property's max length if (prop.length) { // Chop off the front part of the string so it is equal to the length generatedString = generatedString.substring( - generatedString.length - prop.length); + generatedString.length - prop.length + ); } result[name] = generatedString; break; @@ -170,9 +173,9 @@ function Reference(path) { } Reference.prototype.resolveFromContext = function(context) { - var elements = this._path.split('.'); + const elements = this._path.split('.'); - var result = elements.reduce( + const result = elements.reduce( function(obj, prop) { return obj[prop]; }, diff --git a/test/push-manager.test.js b/test/push-manager.test.js index ee32865..16408c5 100644 --- a/test/push-manager.test.js +++ b/test/push-manager.test.js @@ -4,32 +4,32 @@ // License text available at https://opensource.org/licenses/Artistic-2.0 'use strict'; -var async = require('async'); -var expect = require('chai').expect; -var loopback = require('loopback'); -var sinon = require('sinon'); -var PushManager = require('../lib/push-manager'); -var NodeCache = require('node-cache'); - -var mockery = require('./helpers/mockery').stub; -var TestDataBuilder = require('./helpers/test-data-builder'); -var ref = TestDataBuilder.ref; - -var ds = loopback.createDataSource('db', { +const async = require('async'); +const expect = require('chai').expect; +const loopback = require('loopback'); +const sinon = require('sinon'); +const PushManager = require('../lib/push-manager'); +const NodeCache = require('node-cache'); + +const mockery = require('./helpers/mockery').stub; +const TestDataBuilder = require('./helpers/test-data-builder'); +const ref = TestDataBuilder.ref; + +const ds = loopback.createDataSource('db', { connector: loopback.Memory, }); // Application -var Application = loopback.Application; +const Application = loopback.Application; Application.attachTo(ds); // Push Connector -var PushConnector = require('../'); -var Installation = PushConnector.Installation; +const PushConnector = require('../'); +const Installation = PushConnector.Installation; Installation.attachTo(ds); // Notification -var Notification = PushConnector.Notification; +const Notification = PushConnector.Notification; Notification.attachTo(ds); describe('PushManager', function() { @@ -38,7 +38,7 @@ describe('PushManager', function() { beforeEach(Installation.deleteAll.bind(Installation)); afterEach(mockery.tearDown); - var pushManager, context; + let pushManager, context; beforeEach(function(done) { pushManager = new PushManager(); @@ -333,7 +333,7 @@ describe('PushManager', function() { }, function actAndVerify(cb) { - var errorCallback = sinon.spy(); + const errorCallback = sinon.spy(); pushManager.on('error', errorCallback); mockery.pushNotification = function() { @@ -400,7 +400,7 @@ describe('PushManager', function() { // Wait with the check to give the push manager some time // to load all data and push the message setTimeout(function() { - var callsArgs = mockery.pushNotification.args; + const callsArgs = mockery.pushNotification.args; expect(callsArgs, 'number of notifications').to.have.length(2); expect(callsArgs[0]).to.deep.equal([ context.notification, @@ -487,7 +487,7 @@ describe('PushManager', function() { // Wait with the check to give the push manager some time // to load all data and push the message setTimeout(function() { - var callsArgs = mockery.pushNotification.args; + const callsArgs = mockery.pushNotification.args; expect(callsArgs[0], 'number of arguments').to.have.length(2); expect(callsArgs[0]).to.deep.equal([ @@ -555,9 +555,9 @@ describe('PushManager', function() { describe('PushManager applicationsCache', function() { it('settings', function() { - var ttlInSeconds, checkPeriodInSeconds; - ttlInSeconds = checkPeriodInSeconds = 10; - var pm = new PushManager({ + let checkPeriodInSeconds; + const ttlInSeconds = checkPeriodInSeconds = 10; + const pm = new PushManager({ ttlInSeconds: 10, checkPeriodInSeconds: 10, }); @@ -566,13 +566,13 @@ describe('PushManager', function() { }); it('is NodeCache instance', function() { - var pm = new PushManager(); + const pm = new PushManager(); expect(pm.applicationsCache).to.be.a('Object'); expect(pm.applicationsCache).to.be.instanceOf(NodeCache); }); it('has set and get methods', function() { - var pm = new PushManager(); + const pm = new PushManager(); expect(pm.applicationsCache).to.have.property('set'); expect(pm.applicationsCache).to.have.property('get'); }); @@ -600,7 +600,7 @@ describe('PushManager', function() { }, function verify(cb) { - var cacheApp = pushManager.applicationsCache.get( + const cacheApp = pushManager.applicationsCache.get( context.installation.appId ); expect(cacheApp).to.have.property(context.installation.appId); @@ -626,33 +626,33 @@ describe('PushManager model dependencies', function() { }); it('creates properties for dependent models', function() { - var pm = new PushManager(); + const pm = new PushManager(); expect(pm.Installation).to.be.equal(Installation); expect(pm.Notification).to.be.equal(Notification); expect(pm.Application).to.be.equal(Application); }); it('uses subclasses for the dependent models', function() { - var pm = new PushManager(); - var installationModel = Installation.extend('installation', {}); + const pm = new PushManager(); + const installationModel = Installation.extend('installation', {}); expect(pm.Installation).to.be.equal(installationModel); }); it('honors settings', function() { - var pm = new PushManager({ + const pm = new PushManager({ installation: 'myInstallation', }); - var myInstallation = Installation.extend('myInstallation', {}); - var otherInstallation = Installation.extend('otherInstallation', {}); + const myInstallation = Installation.extend('myInstallation', {}); + const otherInstallation = Installation.extend('otherInstallation', {}); expect(pm.Installation).to.be.equal(myInstallation); }); it('supports setters', function() { - var pm = new PushManager({ + const pm = new PushManager({ installation: 'myInstallation', }); - var myInstallation = Installation.extend('myInstallation', {}); - var otherInstallation = Installation.extend('otherInstallation', {}); + const myInstallation = Installation.extend('myInstallation', {}); + const otherInstallation = Installation.extend('otherInstallation', {}); expect(pm.Installation).to.be.equal(myInstallation); pm.Installation = otherInstallation; expect(pm.Installation).to.be.equal(otherInstallation); diff --git a/test/push-notification.test.js b/test/push-notification.test.js index 593b21f..e1de116 100644 --- a/test/push-notification.test.js +++ b/test/push-notification.test.js @@ -4,25 +4,25 @@ // License text available at https://opensource.org/licenses/Artistic-2.0 'use strict'; -var expect = require('chai').expect; -var loopback = require('loopback'); +const expect = require('chai').expect; +const loopback = require('loopback'); -var ds = loopback.createDataSource('db', { +const ds = loopback.createDataSource('db', { connector: loopback.Memory, }); // Application -var Application = loopback.Application; +const Application = loopback.Application; Application.attachTo(ds); // Push Connector -var PushConnector = require('../'); -var PushModel = PushConnector.createPushModel({ +const PushConnector = require('../'); +const PushModel = PushConnector.createPushModel({ dataSource: ds, }); // Installation -var Installation = PushConnector.Installation; +const Installation = PushConnector.Installation; Installation.attachTo(ds); describe('PushNotification', function() { @@ -49,8 +49,8 @@ describe('PushNotification', function() { throw err; } - var application = result; - var deviceToken = + const application = result; + const deviceToken = '6676119dc1ee264f7a32429c56c4e51b0a8b5673d1' + 'd55c431d720bb60b0381d3'; From e44fd82fa05f92b10109c285dc3600fa89a98f4d Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Tue, 10 Sep 2019 10:12:34 -0400 Subject: [PATCH 35/39] update loopback-connector-mongodb --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1d4e76..27ef3aa 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "eslint": "^4.18.2", "eslint-config-loopback": "^13.1.0", "loopback": "^3.0.0", - "loopback-connector-mongodb": "^1.8.0", + "loopback-connector-mongodb": "^5.0.0", "mocha": "^4.0.0", "should": "^6.0.0", "sinon": "^1.14.0" From c7e3734661016ef748bb3f9b8a5f10ddc12d4af7 Mon Sep 17 00:00:00 2001 From: Nora Date: Fri, 15 Nov 2019 20:21:26 -0500 Subject: [PATCH 36/39] chore: improve issue and PR templates --- .github/ISSUE_TEMPLATE.md | 37 ----------------- .github/ISSUE_TEMPLATE/Bug_report.md | 50 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/Feature_request.md | 25 ++++++++++++ .github/ISSUE_TEMPLATE/Question.md | 27 ++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 11 +++++ .github/PULL_REQUEST_TEMPLATE.md | 29 +++++-------- 6 files changed, 124 insertions(+), 55 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/Question.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 795176c..0000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,37 +0,0 @@ - - -# Description/Steps to reproduce - - - -# Link to reproduction sandbox - - - -# Expected result - - - -# Additional information - - diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md new file mode 100644 index 0000000..6bc732a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -0,0 +1,50 @@ +--- +name: Bug report +about: Create a report to help us improve +labels: bug + +--- + + + +## Steps to reproduce + + + +## Current Behavior + + + +## Expected Behavior + + + +## Link to reproduction sandbox + + + +## Additional information + + + +## Related Issues + + + +_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_ diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 0000000..1fd76ba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature request +about: Suggest an idea for this project +labels: feature + +--- + +## Suggestion + + + +## Use Cases + + + +## Examples + + + +## Acceptance criteria + +TBD - will be filled by the team. diff --git a/.github/ISSUE_TEMPLATE/Question.md b/.github/ISSUE_TEMPLATE/Question.md new file mode 100644 index 0000000..eb25195 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Question.md @@ -0,0 +1,27 @@ +--- +name: Question +about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help. +labels: question + +--- + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..9204746 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Report a security vulnerability + url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues + about: Do not report security vulnerabilities using GitHub issues. Please send an email to `reachsl@us.ibm.com` instead. + - name: Get help on StackOverflow + url: https://stackoverflow.com/tags/loopbackjs + about: Please ask and answer questions on StackOverflow. + - name: Join our mailing list + url: https://groups.google.com/forum/#!forum/loopbackjs + about: You can also post your question to our mailing list. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 368cb4c..1446583 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,25 +1,18 @@ -### Description - - -#### Related issues - +Include references to all related GitHub issues and other pull requests, for example: -- connect to +Fixes #123 +Implements #254 +See also #23 +--> -### Checklist +## Checklist - +👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/loopback-component-push) 👈 +- [ ] `npm test` passes on your machine - [ ] New tests added or existing tests modified to cover all changes -- [ ] Code conforms with the [style - guide](http://loopback.io/doc/en/contrib/style-guide.html) +- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html) +- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html) From fcbefb65ef2613f378470118f9feea3a4f175007 Mon Sep 17 00:00:00 2001 From: Diana Lau Date: Mon, 10 Feb 2020 13:55:06 -0500 Subject: [PATCH 37/39] chore: update copyright year --- index.js | 2 +- lib/payload.js | 2 +- lib/providers/apns.js | 2 +- lib/providers/gcm.js | 2 +- lib/providers/index.js | 2 +- lib/push-connector.js | 2 +- lib/push-manager.js | 2 +- models/index.js | 2 +- models/installation.js | 2 +- models/notification.js | 2 +- test/apns.provider.test.js | 2 +- test/device-registration.test.js | 2 +- test/gcm.provider.test.js | 2 +- test/helpers/mockery/apns.mockery.js | 2 +- test/helpers/mockery/gcm.mockery.js | 2 +- test/helpers/mockery/index.js | 2 +- test/helpers/mockery/stub.mockery.js | 2 +- test/helpers/object-mother.js | 2 +- test/helpers/test-data-builder.js | 2 +- test/push-manager.test.js | 2 +- test/push-notification.test.js | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index 87fc797..1d706e1 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/payload.js b/lib/payload.js index 63f721c..487496b 100644 --- a/lib/payload.js +++ b/lib/payload.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/providers/apns.js b/lib/providers/apns.js index 7b2c22a..1b0451b 100644 --- a/lib/providers/apns.js +++ b/lib/providers/apns.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2017. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/providers/gcm.js b/lib/providers/gcm.js index c87d5f8..3c4b9ab 100644 --- a/lib/providers/gcm.js +++ b/lib/providers/gcm.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/providers/index.js b/lib/providers/index.js index 7c447dd..ef81a64 100644 --- a/lib/providers/index.js +++ b/lib/providers/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/push-connector.js b/lib/push-connector.js index 4773e35..60432c9 100644 --- a/lib/push-connector.js +++ b/lib/push-connector.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/lib/push-manager.js b/lib/push-manager.js index ad733d8..a6ec392 100644 --- a/lib/push-manager.js +++ b/lib/push-manager.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/models/index.js b/models/index.js index 69851a5..bde9fbd 100644 --- a/models/index.js +++ b/models/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2015,2016. All Rights Reserved. +// Copyright IBM Corp. 2015,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/models/installation.js b/models/installation.js index 8d4ec47..a6f7cb3 100644 --- a/models/installation.js +++ b/models/installation.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/models/notification.js b/models/notification.js index d2a57b1..10c01c0 100644 --- a/models/notification.js +++ b/models/notification.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/apns.provider.test.js b/test/apns.provider.test.js index 2dd910a..484f366 100644 --- a/test/apns.provider.test.js +++ b/test/apns.provider.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/device-registration.test.js b/test/device-registration.test.js index b01f590..ea421c1 100644 --- a/test/device-registration.test.js +++ b/test/device-registration.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/gcm.provider.test.js b/test/gcm.provider.test.js index d6671d7..4e57191 100644 --- a/test/gcm.provider.test.js +++ b/test/gcm.provider.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/mockery/apns.mockery.js b/test/helpers/mockery/apns.mockery.js index f965299..2522d8e 100644 --- a/test/helpers/mockery/apns.mockery.js +++ b/test/helpers/mockery/apns.mockery.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/mockery/gcm.mockery.js b/test/helpers/mockery/gcm.mockery.js index 67d621d..e101dc9 100644 --- a/test/helpers/mockery/gcm.mockery.js +++ b/test/helpers/mockery/gcm.mockery.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/mockery/index.js b/test/helpers/mockery/index.js index a2cd838..a091715 100644 --- a/test/helpers/mockery/index.js +++ b/test/helpers/mockery/index.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/mockery/stub.mockery.js b/test/helpers/mockery/stub.mockery.js index 94e3d56..94eb3c3 100644 --- a/test/helpers/mockery/stub.mockery.js +++ b/test/helpers/mockery/stub.mockery.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/object-mother.js b/test/helpers/object-mother.js index dbe41fb..57b90c0 100644 --- a/test/helpers/object-mother.js +++ b/test/helpers/object-mother.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/helpers/test-data-builder.js b/test/helpers/test-data-builder.js index f4ce327..d767c4d 100644 --- a/test/helpers/test-data-builder.js +++ b/test/helpers/test-data-builder.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2015,2018. All Rights Reserved. +// Copyright IBM Corp. 2015,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/push-manager.test.js b/test/push-manager.test.js index 16408c5..1c533e5 100644 --- a/test/push-manager.test.js +++ b/test/push-manager.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 diff --git a/test/push-notification.test.js b/test/push-notification.test.js index e1de116..79a8489 100644 --- a/test/push-notification.test.js +++ b/test/push-notification.test.js @@ -1,4 +1,4 @@ -// Copyright IBM Corp. 2013,2018. All Rights Reserved. +// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-component-push // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 From 534dc02e6853545dce193923834b80bcedf732d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Thu, 5 Mar 2020 13:24:20 +0100 Subject: [PATCH 38/39] Update LTS status in README --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 010cf44..b28c103 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ # LoopBack Push Notification Component -![StrongLoop Labs](http://docs.strongloop.com/download/thumbnails/5310165/StrongLoop%20Labs%20Logo%20Cropped.png "StrongLoop Labs") +**⚠️ LoopBack 3 is in Maintenance LTS mode, only critical bugs and critical +security fixes will be provided. (See +[Module Long Term Support Policy](#module-long-term-support-policy) below.)** + +We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as +soon as possible. Refer to our +[Migration Guide](https://loopback.io/doc/en/lb4/migration-overview.html) +for more information on how to upgrade. + +## Overview > StrongLoop Labs projects provide early access to advanced or experimental functionality. In general, these projects may lack usability, completeness, documentation, and robustness, and may be outdated. However, StrongLoop supports these projects: Paying customers can open issues using the StrongLoop customer support system (Zendesk), and community users can report bugs on GitHub. @@ -69,3 +78,15 @@ uses the LoopBack Android SDK to enable and handle push notifications. - https://github.com/argon/node-apn - https://github.com/logicalparadox/apnagent-ios - https://blog.engineyard.com/2013/developing-ios-push-notifications-nodejs + +## Module Long Term Support Policy + +This module adopts the [ +Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy, + with the following End Of Life (EOL) dates: + +| Version | Status | Published | EOL | +| ------- | --------------- | --------- | -------- | +| 3.x | Maintenance LTS | Dec 2016 | Dec 2020 | + +Learn more about our LTS plan in [docs](https://loopback.io/doc/en/contrib/Long-term-support.html). From ff5299fe26ebdfa2f5a9eef32b21449597251908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Fri, 6 Mar 2020 17:22:07 +0100 Subject: [PATCH 39/39] 3.5.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update LTS status in README (Miroslav Bajtoš) * chore: update copyright year (Diana Lau) * chore: improve issue and PR templates (Nora) * update loopback-connector-mongodb (Diana Lau) * update eslint-config-loopback and fix violations (Nora) * drop support for node.js 6 (Nora) * update eslint dependency (Nora) --- CHANGES.md | 18 ++++++++++++++++++ package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index c6846fa..438d5e0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,21 @@ +2020-03-06, Version 3.5.0 +========================= + + * Update LTS status in README (Miroslav Bajtoš) + + * chore: update copyright year (Diana Lau) + + * chore: improve issue and PR templates (Nora) + + * update loopback-connector-mongodb (Diana Lau) + + * update eslint-config-loopback and fix violations (Nora) + + * drop support for node.js 6 (Nora) + + * update eslint dependency (Nora) + + 2019-07-09, Version 3.4.1 ========================= diff --git a/package.json b/package.json index 27ef3aa..dda34b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-component-push", - "version": "3.4.1", + "version": "3.5.0", "description": "Loopback Push Notification", "keywords": [ "StrongLoop Labs",