@parse/push-adapter 5.0.1 → 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 +229 -0
- package/lib/ParsePushAdapter.js +14 -2
- package/package.json +3 -2
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
|
+
}
|
package/lib/ParsePushAdapter.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
"
|
|
45
|
+
"firebase-admin": "11.10.1",
|
|
46
|
+
"npmlog": "7.0.1",
|
|
46
47
|
"parse": "4.2.0"
|
|
47
48
|
},
|
|
48
49
|
"engines": {
|