diff --git a/lib/EXPO.js b/lib/EXPO.js index 1a51dff6..09ffa01a 100755 --- a/lib/EXPO.js +++ b/lib/EXPO.js @@ -5,250 +5,330 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports["default"] = EXPO; -var _parse = _interopRequireDefault(require("parse")); +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; -var _npmlog = _interopRequireDefault(require("npmlog")); +exports.default = EXPO; -var _expoServerSdk = _interopRequireDefault(require("expo-server-sdk")); +var _parse = require('parse'); -require("babel-polyfill"); +var _parse2 = _interopRequireDefault(_parse); -var _PushAdapterUtils = require("./PushAdapterUtils"); +var _npmlog = require('npmlog'); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } +var _npmlog2 = _interopRequireDefault(_npmlog); -function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } +var _expoServerSdk = require('expo-server-sdk'); -function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +var _expoServerSdk2 = _interopRequireDefault(_expoServerSdk); -function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } +require('babel-polyfill'); -function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } +var _PushAdapterUtils = require('./PushAdapterUtils'); -function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - -function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } - -function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } var LOG_PREFIX = 'parse-server-push-adapter EXPO'; var GCMRegistrationTokensMax = 1000; var EXPOTimeToLiveMax = 4 * 7 * 24 * 60 * 60; // GCM allows a max of 4 weeks function EXPO(args) { - this.sender = new _expoServerSdk["default"](); + this.sender = new _expoServerSdk2.default(); + this.webhookurl = args.webhookurl; } + /** * Send gcm request. * @param {Object} data The data we need to send, the format is the same with api request body * @param {Array} devices A array of devices * @returns {Object} A promise which is resolved after we get results from gcm */ - - EXPO.prototype.send = function (data, devices) { var _this = this; - var pushId = (0, _PushAdapterUtils.randomString)(10); // Make a new array - + var pushId = (0, _PushAdapterUtils.randomString)(10); + // Make a new array devices = devices.slice(0); var timestamp = Date.now(); - var expirationTime; // We handle the expiration_time convertion in push.js, so expiration_time is a valid date - // in Unix epoch time in milliseconds here + var expirationTime = void 0; + // We handle the expiration_time convertion in push.js, so expiration_time is a valid date + // in Unix epoch time in milliseconds here if (data['expiration_time']) { expirationTime = data['expiration_time']; } - var messages = []; // Build a device map + var messages = []; + // Build a device map var devicesMap = devices.reduce(function (memo, device) { memo[device.deviceToken] = device; return memo; }, {}); + var deviceTokens = Object.keys(devicesMap); + var promises = deviceTokens.map(function () { - return new _parse["default"].Promise(); + return new _parse2.default.Promise(); }); var registrationTokens = deviceTokens; var length = registrationTokens.length; - - _npmlog["default"].verbose(LOG_PREFIX, "sending to ".concat(length, " ").concat(length > 1 ? 'devices' : 'device')); - + _npmlog2.default.verbose(LOG_PREFIX, 'sending to ' + length + ' ' + (length > 1 ? 'devices' : 'device')); var notificationData = { body: data.data.alert, title: data.data.title, badge: data.data.badge ? data.data.badge : 1, data: data.data }; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; - for (var _i = 0, _registrationTokens = registrationTokens; _i < _registrationTokens.length; _i++) { - var pushToken = _registrationTokens[_i]; + try { + for (var _iterator = registrationTokens[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var pushToken = _step.value; - if (!_expoServerSdk["default"].isExpoPushToken(pushToken)) { - console.error("Push token ".concat(pushToken, " is not a valid Expo push token")); - continue; + if (!_expoServerSdk2.default.isExpoPushToken(pushToken)) { + console.error('Push token ' + pushToken + ' is not a valid Expo push token'); + continue; + } + messages.push({ + to: pushToken, + sound: 'default', + body: notificationData.body, + title: notificationData.title, + badge: notificationData.badge, + data: notificationData, + ttl: EXPOTimeToLiveMax, + priority: 'high', + channelId: 'fif-notifications', + 'content-available': 1, + user: devicesMap[pushToken] && devicesMap[pushToken].user ? devicesMap[pushToken].user : 'NA' + }); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } } - - messages.push({ - to: pushToken, - sound: 'default', - body: notificationData.body, - title: notificationData.title, - badge: notificationData.badge, - data: notificationData, - ttl: EXPOTimeToLiveMax, - priority: 'high', - channelId: 'fif-notifications', - 'content-available': 1 - }); } var chunks = this.sender.chunkPushNotifications(messages); var tickets = []; - _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { - var _iterator, _step, chunkList, _iterator2, _step2, chunk, ticketChunk; + var _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, chunkList, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, chunk, ticketChunk; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: - _iterator = _createForOfIteratorHelper(chunks); - _context.prev = 1; - - _iterator.s(); - - case 3: - if ((_step = _iterator.n()).done) { - _context.next = 32; + _iteratorNormalCompletion2 = true; + _didIteratorError2 = false; + _iteratorError2 = undefined; + _context.prev = 3; + _iterator2 = chunks[Symbol.iterator](); + + case 5: + if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) { + _context.next = 44; break; } - chunkList = _step.value; - _iterator2 = _createForOfIteratorHelper(chunkList); - _context.prev = 6; - - _iterator2.s(); + chunkList = _step2.value; + _iteratorNormalCompletion3 = true; + _didIteratorError3 = false; + _iteratorError3 = undefined; + _context.prev = 10; + _iterator3 = chunkList[Symbol.iterator](); - case 8: - if ((_step2 = _iterator2.n()).done) { - _context.next = 22; + case 12: + if (_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done) { + _context.next = 27; break; } - chunk = _step2.value; - _context.prev = 10; - _context.next = 13; + chunk = _step3.value; + _context.prev = 14; + _context.next = 17; return _this.sender.sendPushNotificationsAsync([chunk]); - case 13: + case 17: ticketChunk = _context.sent; - tickets.push.apply(tickets, _toConsumableArray(ticketChunk)); - _context.next = 20; + + //Post Reciepts to callback + if (ticketChunk.length) { + tickets.push(_extends({}, ticketChunk[0], chunk)); + } + _context.next = 24; break; - case 17: - _context.prev = 17; - _context.t0 = _context["catch"](10); + case 21: + _context.prev = 21; + _context.t0 = _context['catch'](14); + console.error(_context.t0); - case 20: - _context.next = 8; + case 24: + _iteratorNormalCompletion3 = true; + _context.next = 12; break; - case 22: - _context.next = 27; + case 27: + _context.next = 33; break; - case 24: - _context.prev = 24; - _context.t1 = _context["catch"](6); + case 29: + _context.prev = 29; + _context.t1 = _context['catch'](10); + _didIteratorError3 = true; + _iteratorError3 = _context.t1; - _iterator2.e(_context.t1); + case 33: + _context.prev = 33; + _context.prev = 34; - case 27: - _context.prev = 27; + if (!_iteratorNormalCompletion3 && _iterator3.return) { + _iterator3.return(); + } + + case 36: + _context.prev = 36; - _iterator2.f(); + if (!_didIteratorError3) { + _context.next = 39; + break; + } - return _context.finish(27); + throw _iteratorError3; - case 30: - _context.next = 3; + case 39: + return _context.finish(36); + + case 40: + return _context.finish(33); + + case 41: + _iteratorNormalCompletion2 = true; + _context.next = 5; break; - case 32: - _context.next = 37; + case 44: + _context.next = 50; break; - case 34: - _context.prev = 34; - _context.t2 = _context["catch"](1); + case 46: + _context.prev = 46; + _context.t2 = _context['catch'](3); + _didIteratorError2 = true; + _iteratorError2 = _context.t2; - _iterator.e(_context.t2); + case 50: + _context.prev = 50; + _context.prev = 51; - case 37: - _context.prev = 37; + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } - _iterator.f(); + case 53: + _context.prev = 53; - return _context.finish(37); + if (!_didIteratorError2) { + _context.next = 56; + break; + } - case 40: - case "end": + throw _iteratorError2; + + case 56: + return _context.finish(53); + + case 57: + return _context.finish(50); + + case 58: + if (tickets.length) { + (0, _PushAdapterUtils.handleCallback)(_this.webhookurl, tickets); + } + + case 59: + case 'end': return _context.stop(); } } - }, _callee, null, [[1, 34, 37, 40], [6, 24, 27, 30], [10, 17]]); + }, _callee, _this, [[3, 46, 50, 58], [10, 29, 33, 41], [14, 21], [34,, 36, 40], [51,, 53, 57]]); }))(); var receiptIds = []; + var _iteratorNormalCompletion4 = true; + var _didIteratorError4 = false; + var _iteratorError4 = undefined; - for (var _i2 = 0, _tickets = tickets; _i2 < _tickets.length; _i2++) { - var ticket = _tickets[_i2]; + try { + for (var _iterator4 = tickets[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { + var ticket = _step4.value; - if (ticket.id) { - receiptIds.push(ticket.id); + if (ticket.id) { + receiptIds.push(ticket.id); + } + } + } catch (err) { + _didIteratorError4 = true; + _iteratorError4 = err; + } finally { + try { + if (!_iteratorNormalCompletion4 && _iterator4.return) { + _iterator4.return(); + } + } finally { + if (_didIteratorError4) { + throw _iteratorError4; + } } } var receiptIdChunks = this.sender.chunkPushNotificationReceiptIds(receiptIds); - _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { - var _iterator3, _step3, chunk, receipts; + var _iteratorNormalCompletion5, _didIteratorError5, _iteratorError5, _iterator5, _step5, chunk, receipts; return regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: - _iterator3 = _createForOfIteratorHelper(receiptIdChunks); - _context2.prev = 1; - - _iterator3.s(); - - case 3: - if ((_step3 = _iterator3.n()).done) { - _context2.next = 18; + _iteratorNormalCompletion5 = true; + _didIteratorError5 = false; + _iteratorError5 = undefined; + _context2.prev = 3; + _iterator5 = receiptIdChunks[Symbol.iterator](); + + case 5: + if (_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done) { + _context2.next = 21; break; } - chunk = _step3.value; - _context2.prev = 5; - _context2.next = 8; + chunk = _step5.value; + _context2.prev = 7; + _context2.next = 10; return _this.sender.getPushNotificationReceiptsAsync(chunk); - case 8: + case 10: receipts = _context2.sent; - console.log(receipts); // The receipts specify whether Apple or Google successfully received the - // notification and information about an error, if one occurred. + console.log(receipts); + + // The receipts specify whether Apple or Google successfully received the + // notification and information about an error, if one occurred. receipts.forEach(function (receipt, index) { var promise = promises[index]; var result = results ? results[index] : undefined; @@ -259,51 +339,68 @@ EXPO.prototype.send = function (data, devices) { multicast_id: 'multicast_id', response: receipt }; - if (receipt.status === 'error') { resolution.transmitted = false; } else { resolution.transmitted = true; } - promise.resolve(resolution); }); - _context2.next = 16; + _context2.next = 18; break; - case 13: - _context2.prev = 13; - _context2.t0 = _context2["catch"](5); - console.error(_context2.t0); + case 15: + _context2.prev = 15; + _context2.t0 = _context2['catch'](7); - case 16: - _context2.next = 3; - break; + console.error(_context2.t0); case 18: - _context2.next = 23; + _iteratorNormalCompletion5 = true; + _context2.next = 5; break; - case 20: - _context2.prev = 20; - _context2.t1 = _context2["catch"](1); - - _iterator3.e(_context2.t1); + case 21: + _context2.next = 27; + break; case 23: _context2.prev = 23; + _context2.t1 = _context2['catch'](3); + _didIteratorError5 = true; + _iteratorError5 = _context2.t1; + + case 27: + _context2.prev = 27; + _context2.prev = 28; + + if (!_iteratorNormalCompletion5 && _iterator5.return) { + _iterator5.return(); + } + + case 30: + _context2.prev = 30; - _iterator3.f(); + if (!_didIteratorError5) { + _context2.next = 33; + break; + } + + throw _iteratorError5; - return _context2.finish(23); + case 33: + return _context2.finish(30); + + case 34: + return _context2.finish(27); - case 26: - case "end": + case 35: + case 'end': return _context2.stop(); } } - }, _callee2, null, [[1, 20, 23, 26], [5, 13]]); + }, _callee2, _this, [[3, 23, 27, 35], [7, 15], [28,, 30, 34]]); }))(); - return _parse["default"].Promise.when(promises); -}; + return _parse2.default.Promise.when(promises); +}; \ No newline at end of file diff --git a/lib/PushAdapterUtils.js b/lib/PushAdapterUtils.js index 5282659e..3d285943 100755 --- a/lib/PushAdapterUtils.js +++ b/lib/PushAdapterUtils.js @@ -5,9 +5,12 @@ Object.defineProperty(exports, "__esModule", { }); exports.classifyInstallations = classifyInstallations; exports.randomString = randomString; +exports.handleCallback = handleCallback; var _crypto = require('crypto'); +var axios = require('axios'); + /**g * Classify the device token of installations based on its device type. * @param {Object} installations An array of installations @@ -59,7 +62,8 @@ function classifyInstallations(installations, validPushTypes) { devices.push({ deviceToken: installation.deviceToken, deviceType: installation.deviceType, - appIdentifier: installation.appIdentifier + appIdentifier: installation.appIdentifier, + user: installation.user }); } } @@ -92,4 +96,11 @@ function randomString(size) { objectId += chars[bytes.readUInt8(i) % chars.length]; } return objectId; +} + +//Post Reciepts to callback url +function handleCallback(webhookurl, pushStatusData) { + axios.post(webhookurl, { 'pushStatusData': pushStatusData }).then(function (response) { + console.log('Callback request completed' + response); + }); } \ No newline at end of file diff --git a/package.json b/package.json index f6e79f23..d373a96b 100755 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "babel-polyfill": "^6.26.0", "apn": "^1.7.8", + "axios": "^1.6.0", "babel-preset-es2017": "^6.14.0", "expo-server-sdk": "^3.2.0", "node-gcm": "^0.14.0", diff --git a/src/EXPO.js b/src/EXPO.js index 60a11065..615c2dde 100755 --- a/src/EXPO.js +++ b/src/EXPO.js @@ -6,7 +6,7 @@ import Parse from 'parse'; import log from 'npmlog'; import Expo from 'expo-server-sdk'; import "babel-polyfill"; -import { randomString } from './PushAdapterUtils'; +import { randomString, handleCallback } from './PushAdapterUtils'; const LOG_PREFIX = 'parse-server-push-adapter EXPO'; const GCMRegistrationTokensMax = 1000; @@ -14,6 +14,7 @@ const EXPOTimeToLiveMax = 4 * 7 * 24 * 60 * 60; // GCM allows a max of 4 weeks export default function EXPO(args) { this.sender = new Expo(); + this.webhookurl = args.webhookurl; } /** @@ -70,25 +71,31 @@ EXPO.prototype.send = function(data, devices) { ttl: EXPOTimeToLiveMax, priority: 'high', channelId: 'fif-notifications', - 'content-available': 1 + 'content-available': 1, + user: devicesMap[pushToken] && devicesMap[pushToken].user ? devicesMap[pushToken].user : 'NA' }); } - let chunks = this.sender.chunkPushNotifications(messages); let tickets = []; - (async () => { for (let chunkList of chunks) { for (let chunk of chunkList) { try { - let ticketChunk = await this.sender.sendPushNotificationsAsync([chunk]); - tickets.push(...ticketChunk); + let ticketChunk = await this.sender.sendPushNotificationsAsync([chunk]); + //Post Reciepts to callback + if(ticketChunk.length) { + tickets.push({...ticketChunk[0], ...chunk}); + } } catch (error) { console.error(error); } } } - })(); + if(tickets.length) { + handleCallback(this.webhookurl, tickets); + } + } + )(); let receiptIds = []; for (let ticket of tickets) { @@ -130,4 +137,4 @@ EXPO.prototype.send = function(data, devices) { })(); return Parse.Promise.when(promises); -} +}; diff --git a/src/PushAdapterUtils.js b/src/PushAdapterUtils.js index 9abb1e83..f5678af3 100755 --- a/src/PushAdapterUtils.js +++ b/src/PushAdapterUtils.js @@ -1,4 +1,5 @@ import { randomBytes } from 'crypto'; +var axios = require('axios'); /**g * Classify the device token of installations based on its device type. @@ -22,7 +23,8 @@ export function classifyInstallations(installations, validPushTypes) { devices.push({ deviceToken: installation.deviceToken, deviceType: installation.deviceType, - appIdentifier: installation.appIdentifier + appIdentifier: installation.appIdentifier, + user: installation.user, }); } } @@ -43,3 +45,11 @@ export function randomString(size) { } return objectId; } + +//Post Reciepts to callback url +export function handleCallback(webhookurl, pushStatusData){ + axios.post(webhookurl, {'pushStatusData' : pushStatusData}) + .then(function (response) { + console.log('Callback request completed'+ response); + }); +}