@parse/push-adapter 5.0.2 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/FCM.js ADDED
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ 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; };
8
+
9
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
10
+
11
+ exports.default = FCM;
12
+
13
+ var _parse = require('parse');
14
+
15
+ var _parse2 = _interopRequireDefault(_parse);
16
+
17
+ var _npmlog = require('npmlog');
18
+
19
+ var _npmlog2 = _interopRequireDefault(_npmlog);
20
+
21
+ var _app = require('firebase-admin/app');
22
+
23
+ var _messaging = require('firebase-admin/messaging');
24
+
25
+ var _PushAdapterUtils = require('./PushAdapterUtils');
26
+
27
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
+
29
+ var LOG_PREFIX = 'parse-server-push-adapter FCM';
30
+ var FCMRegistrationTokensMax = 500;
31
+ var FCMTimeToLiveMax = 4 * 7 * 24 * 60 * 60; // FCM allows a max of 4 weeks
32
+
33
+ function FCM(args) {
34
+ if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) !== 'object' || !args.firebaseServiceAccount) {
35
+ throw new _parse2.default.Error(_parse2.default.Error.PUSH_MISCONFIGURED, 'FCM Configuration is invalid');
36
+ }
37
+
38
+ var app = void 0;
39
+ if ((0, _app.getApps)().length === 0) {
40
+ app = (0, _app.initializeApp)({ credential: (0, _app.cert)(args.firebaseServiceAccount) });
41
+ } else {
42
+ app = (0, _app.getApp)();
43
+ }
44
+ this.sender = (0, _messaging.getMessaging)(app);
45
+ }
46
+
47
+ FCM.FCMRegistrationTokensMax = FCMRegistrationTokensMax;
48
+
49
+ /**
50
+ * Send fcm request.
51
+ * @param {Object} data The data we need to send, the format is the same with api request body
52
+ * @param {Array} devices A array of devices
53
+ * @returns {Object} Array of resolved promises
54
+ */
55
+
56
+ FCM.prototype.send = function (data, devices) {
57
+ var _this = this;
58
+
59
+ if (!data || !devices || !Array.isArray(devices)) {
60
+ _npmlog2.default.warn(LOG_PREFIX, 'invalid push payload');
61
+ return;
62
+ }
63
+
64
+ // We can only have 500 recepients per send, so we need to slice devices to
65
+ // chunk if necessary
66
+ var slices = sliceDevices(devices, FCM.FCMRegistrationTokensMax);
67
+
68
+ var sendToDeviceSlice = function sendToDeviceSlice(deviceSlice) {
69
+ var pushId = (0, _PushAdapterUtils.randomString)(10);
70
+ var timestamp = Date.now();
71
+
72
+ // Build a device map
73
+ var devicesMap = deviceSlice.reduce(function (memo, device) {
74
+ memo[device.deviceToken] = device;
75
+ return memo;
76
+ }, {});
77
+
78
+ var deviceTokens = Object.keys(devicesMap);
79
+ var fcmPayload = generateFCMPayload(data, pushId, timestamp, deviceTokens);
80
+ var length = deviceTokens.length;
81
+ _npmlog2.default.info(LOG_PREFIX, 'sending push to ' + length + ' devices');
82
+
83
+ return _this.sender.sendEachForMulticast(fcmPayload.data).then(function (response) {
84
+ var promises = [];
85
+ var failedTokens = [];
86
+ var successfulTokens = [];
87
+
88
+ response.responses.forEach(function (resp, idx) {
89
+ if (resp.success) {
90
+ successfulTokens.push(deviceTokens[idx]);
91
+ promises.push(createSuccessfulPromise(deviceTokens[idx], devicesMap[deviceTokens[idx]].deviceType));
92
+ } else {
93
+ failedTokens.push(deviceTokens[idx]);
94
+ promises.push(createErrorPromise(deviceTokens[idx], devicesMap[deviceTokens[idx]].deviceType, resp.error));
95
+ _npmlog2.default.error(LOG_PREFIX, 'failed to send to ' + deviceTokens[idx] + ' with error: ' + JSON.stringify(resp.error));
96
+ }
97
+ });
98
+
99
+ if (failedTokens.length) {
100
+ _npmlog2.default.error(LOG_PREFIX, 'tokens with failed pushes: ' + JSON.stringify(failedTokens));
101
+ }
102
+
103
+ if (successfulTokens.length) {
104
+ _npmlog2.default.verbose(LOG_PREFIX, 'tokens with successful pushes: ' + JSON.stringify(successfulTokens));
105
+ }
106
+
107
+ return Promise.all(promises);
108
+ });
109
+ };
110
+
111
+ var allPromises = Promise.all(slices.map(sendToDeviceSlice)).catch(function (err) {
112
+ _npmlog2.default.error(LOG_PREFIX, 'error sending push: ' + err);
113
+ });
114
+
115
+ return allPromises;
116
+ };
117
+
118
+ /**
119
+ * Generate the fcm payload from the data we get from api request.
120
+ * @param {Object} requestData The request body
121
+ * @param {String} pushId A random string
122
+ * @param {Number} timeStamp A number in milliseconds since the Unix Epoch
123
+ * @returns {Object} A payload for FCM
124
+ */
125
+ function generateFCMPayload(requestData, pushId, timeStamp, deviceTokens) {
126
+ delete requestData['where'];
127
+
128
+ var payloadToUse = {
129
+ data: {},
130
+ push_id: pushId,
131
+ time: new Date(timeStamp).toISOString()
132
+ };
133
+
134
+ // Use rawPayload instead of the GCM implementation if it exists
135
+ if (requestData.hasOwnProperty('rawPayload')) {
136
+ payloadToUse.data = _extends({}, requestData.rawPayload, {
137
+ tokens: deviceTokens
138
+ });
139
+ } else {
140
+ // Android payload according to GCM implementation
141
+ var androidPayload = {
142
+ android: {
143
+ priority: 'high'
144
+ },
145
+ tokens: deviceTokens
146
+ };
147
+
148
+ if (requestData.hasOwnProperty('notification')) {
149
+ androidPayload.notification = requestData.notification;
150
+ }
151
+
152
+ if (requestData.hasOwnProperty('data')) {
153
+ androidPayload.data = requestData.data;
154
+ }
155
+
156
+ if (requestData['expiration_time']) {
157
+ var expirationTime = requestData['expiration_time'];
158
+ // Convert to seconds
159
+ var timeToLive = Math.floor((expirationTime - timeStamp) / 1000);
160
+ if (timeToLive < 0) {
161
+ timeToLive = 0;
162
+ }
163
+ if (timeToLive >= FCMTimeToLiveMax) {
164
+ timeToLive = FCMTimeToLiveMax;
165
+ }
166
+
167
+ androidPayload.android.ttl = timeToLive;
168
+ }
169
+
170
+ payloadToUse.data = androidPayload;
171
+ }
172
+
173
+ return payloadToUse;
174
+ }
175
+
176
+ /**
177
+ * Slice a list of devices to several list of devices with fixed chunk size.
178
+ * @param {Array} devices An array of devices
179
+ * @param {Number} chunkSize The size of the a chunk
180
+ * @returns {Array} An array which contains several arrays of devices with fixed chunk size
181
+ */
182
+ function sliceDevices(devices, chunkSize) {
183
+ var chunkDevices = [];
184
+ while (devices.length > 0) {
185
+ chunkDevices.push(devices.splice(0, chunkSize));
186
+ }
187
+ return chunkDevices;
188
+ }
189
+
190
+ /**
191
+ * Creates an errorPromise for return.
192
+ *
193
+ * @param {String} token Device-Token
194
+ * @param {String} deviceType Device-Type
195
+ * @param {String} errorMessage ErrrorMessage as string
196
+ */
197
+ function createErrorPromise(token, deviceType, errorMessage) {
198
+ return Promise.resolve({
199
+ transmitted: false,
200
+ device: {
201
+ deviceToken: token,
202
+ deviceType: deviceType
203
+ },
204
+ response: { error: errorMessage }
205
+ });
206
+ }
207
+
208
+ /**
209
+ * Creates an successfulPromise for return.
210
+ *
211
+ * @param {String} token Device-Token
212
+ * @param {String} deviceType Device-Type
213
+ */
214
+ function createSuccessfulPromise(token, deviceType) {
215
+ return Promise.resolve({
216
+ transmitted: true,
217
+ device: {
218
+ deviceToken: token,
219
+ deviceType: deviceType
220
+ }
221
+ });
222
+ }
223
+
224
+ FCM.generateFCMPayload = generateFCMPayload;
225
+
226
+ /* istanbul ignore else */
227
+ if (process.env.TESTING) {
228
+ FCM.sliceDevices = sliceDevices;
229
+ }
@@ -22,6 +22,10 @@ var _GCM = require('./GCM');
22
22
 
23
23
  var _GCM2 = _interopRequireDefault(_GCM);
24
24
 
25
+ var _FCM = require('./FCM');
26
+
27
+ var _FCM2 = _interopRequireDefault(_FCM);
28
+
25
29
  var _PushAdapterUtils = require('./PushAdapterUtils');
26
30
 
27
31
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -62,11 +66,19 @@ var ParsePushAdapter = function () {
62
66
  case 'ios':
63
67
  case 'tvos':
64
68
  case 'osx':
65
- this.senderMap[pushType] = new _APNS2.default(pushConfig[pushType]);
69
+ if (pushConfig[pushType].hasOwnProperty('firebaseServiceAccount')) {
70
+ this.senderMap[pushType] = new _FCM2.default(pushConfig[pushType]);
71
+ } else {
72
+ this.senderMap[pushType] = new _APNS2.default(pushConfig[pushType]);
73
+ }
66
74
  break;
67
75
  case 'android':
68
76
  case 'fcm':
69
- this.senderMap[pushType] = new _GCM2.default(pushConfig[pushType]);
77
+ if (pushConfig[pushType].hasOwnProperty('firebaseServiceAccount')) {
78
+ this.senderMap[pushType] = new _FCM2.default(pushConfig[pushType]);
79
+ } else {
80
+ this.senderMap[pushType] = new _GCM2.default(pushConfig[pushType]);
81
+ }
70
82
  break;
71
83
  }
72
84
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parse/push-adapter",
3
- "version": "5.0.2",
3
+ "version": "5.1.0",
4
4
  "description": "Base parse-server-push-adapter",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -42,7 +42,8 @@
42
42
  "dependencies": {
43
43
  "@parse/node-apn": "6.0.1",
44
44
  "@parse/node-gcm": "1.0.2",
45
- "npmlog": "4.1.2",
45
+ "firebase-admin": "11.10.1",
46
+ "npmlog": "7.0.1",
46
47
  "parse": "4.2.0"
47
48
  },
48
49
  "engines": {