@parse/push-adapter 6.11.0 → 7.0.1
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/package.json +12 -13
- package/src/ParsePushAdapter.js +5 -5
- package/src/index.js +2 -2
- package/src/GCM.js +0 -179
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parse/push-adapter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "Base parse-server-push-adapter",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,28 +24,27 @@
|
|
|
24
24
|
"author": "Parse",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@parse/node-apn": "
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"firebase-admin": "13.2.0",
|
|
27
|
+
"@parse/node-apn": "7.0.0",
|
|
28
|
+
"expo-server-sdk": "3.15.0",
|
|
29
|
+
"firebase-admin": "13.6.0",
|
|
31
30
|
"npmlog": "7.0.1",
|
|
32
|
-
"parse": "
|
|
31
|
+
"parse": "7.0.2",
|
|
33
32
|
"web-push": "3.6.7"
|
|
34
33
|
},
|
|
35
34
|
"devDependencies": {
|
|
36
|
-
"@eslint/js": "9.
|
|
35
|
+
"@eslint/js": "9.38.0",
|
|
37
36
|
"@semantic-release/changelog": "6.0.3",
|
|
38
37
|
"@semantic-release/commit-analyzer": "13.0.1",
|
|
39
38
|
"@semantic-release/git": "10.0.1",
|
|
40
|
-
"@semantic-release/github": "
|
|
41
|
-
"@semantic-release/npm": "
|
|
42
|
-
"@semantic-release/release-notes-generator": "14.0
|
|
39
|
+
"@semantic-release/github": "12.0.2",
|
|
40
|
+
"@semantic-release/npm": "13.1.2",
|
|
41
|
+
"@semantic-release/release-notes-generator": "14.1.0",
|
|
43
42
|
"c8": "10.1.3",
|
|
44
43
|
"codecov": "3.8.3",
|
|
45
|
-
"eslint": "9.
|
|
46
|
-
"jasmine": "5.
|
|
44
|
+
"eslint": "9.39.1",
|
|
45
|
+
"jasmine": "5.12.0",
|
|
47
46
|
"jasmine-spec-reporter": "7.0.0",
|
|
48
|
-
"semantic-release": "
|
|
47
|
+
"semantic-release": "25.0.2"
|
|
49
48
|
},
|
|
50
49
|
"engines": {
|
|
51
50
|
"node": "18 || 20 || 22"
|
package/src/ParsePushAdapter.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import Parse from 'parse';
|
|
3
3
|
import log from 'npmlog';
|
|
4
4
|
import APNS from './APNS.js';
|
|
5
|
-
import GCM from './GCM.js';
|
|
6
5
|
import FCM from './FCM.js';
|
|
7
6
|
import WEB from './WEB.js';
|
|
8
7
|
import EXPO from './EXPO.js';
|
|
@@ -51,7 +50,8 @@ export default class ParsePushAdapter {
|
|
|
51
50
|
if (pushConfig[pushType].hasOwnProperty('firebaseServiceAccount')) {
|
|
52
51
|
this.senderMap[pushType] = new FCM(pushConfig[pushType], 'android');
|
|
53
52
|
} else {
|
|
54
|
-
|
|
53
|
+
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
|
|
54
|
+
'Firebase service account is required for Android/FCM push notifications');
|
|
55
55
|
}
|
|
56
56
|
break;
|
|
57
57
|
}
|
|
@@ -73,14 +73,14 @@ export default class ParsePushAdapter {
|
|
|
73
73
|
const sender = this.senderMap[pushType];
|
|
74
74
|
const devices = deviceMap[pushType];
|
|
75
75
|
|
|
76
|
-
if(Array.isArray(devices) && devices.length > 0) {
|
|
76
|
+
if (Array.isArray(devices) && devices.length > 0) {
|
|
77
77
|
if (!sender) {
|
|
78
78
|
log.verbose(LOG_PREFIX, `Can not find sender for push type ${pushType}, ${data}`)
|
|
79
79
|
const results = devices.map((device) => {
|
|
80
80
|
return Promise.resolve({
|
|
81
81
|
device,
|
|
82
82
|
transmitted: false,
|
|
83
|
-
response: {'error': `Can not find sender for push type ${pushType}, ${data}`}
|
|
83
|
+
response: { 'error': `Can not find sender for push type ${pushType}, ${data}` }
|
|
84
84
|
})
|
|
85
85
|
});
|
|
86
86
|
sendPromises.push(Promise.all(results));
|
|
@@ -91,7 +91,7 @@ export default class ParsePushAdapter {
|
|
|
91
91
|
}
|
|
92
92
|
return Promise.all(sendPromises).then((promises) => {
|
|
93
93
|
// flatten all
|
|
94
|
-
return [].
|
|
94
|
+
return [...promises].flat(2);
|
|
95
95
|
})
|
|
96
96
|
}
|
|
97
97
|
}
|
package/src/index.js
CHANGED
|
@@ -12,11 +12,11 @@ if (booleanParser(process.env.VERBOSE || process.env.VERBOSE_PARSE_SERVER_PUSH_A
|
|
|
12
12
|
/* c8 ignore stop */
|
|
13
13
|
|
|
14
14
|
import ParsePushAdapter from './ParsePushAdapter.js';
|
|
15
|
-
import GCM from './GCM.js';
|
|
16
15
|
import APNS from './APNS.js';
|
|
17
16
|
import WEB from './WEB.js';
|
|
17
|
+
import FCM from './FCM.js';
|
|
18
18
|
import EXPO from './EXPO.js';
|
|
19
19
|
import * as utils from './PushAdapterUtils.js';
|
|
20
20
|
|
|
21
21
|
export default ParsePushAdapter;
|
|
22
|
-
export { ParsePushAdapter, APNS,
|
|
22
|
+
export { ParsePushAdapter, APNS, WEB, EXPO, FCM, utils };
|
package/src/GCM.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import Parse from 'parse';
|
|
4
|
-
import log from 'npmlog';
|
|
5
|
-
import gcm from '@parse/node-gcm';
|
|
6
|
-
import { randomString } from './PushAdapterUtils.js';
|
|
7
|
-
|
|
8
|
-
const LOG_PREFIX = 'parse-server-push-adapter GCM';
|
|
9
|
-
const GCMTimeToLiveMax = 4 * 7 * 24 * 60 * 60; // GCM allows a max of 4 weeks
|
|
10
|
-
const GCMRegistrationTokensMax = 1000;
|
|
11
|
-
|
|
12
|
-
export default function GCM(args) {
|
|
13
|
-
if (typeof args !== 'object' || !args.apiKey) {
|
|
14
|
-
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
|
|
15
|
-
'GCM Configuration is invalid');
|
|
16
|
-
}
|
|
17
|
-
this.sender = new gcm.Sender(args.apiKey, args.requestOptions);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
GCM.GCMRegistrationTokensMax = GCMRegistrationTokensMax;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Send gcm request.
|
|
24
|
-
* @param {Object} data The data we need to send, the format is the same with api request body
|
|
25
|
-
* @param {Array} devices A array of devices
|
|
26
|
-
* @returns {Object} A promise which is resolved after we get results from gcm
|
|
27
|
-
*/
|
|
28
|
-
GCM.prototype.send = function(data, devices) {
|
|
29
|
-
if (!data || !devices || !Array.isArray(devices)) {
|
|
30
|
-
log.warn(LOG_PREFIX, 'invalid push payload');
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const pushId = randomString(10);
|
|
34
|
-
// Make a new array
|
|
35
|
-
devices = devices.slice(0);
|
|
36
|
-
const timestamp = Date.now();
|
|
37
|
-
// For android, we can only have 1000 recepients per send, so we need to slice devices to
|
|
38
|
-
// chunk if necessary
|
|
39
|
-
const slices = sliceDevices(devices, GCM.GCMRegistrationTokensMax);
|
|
40
|
-
if (slices.length > 1) {
|
|
41
|
-
log.verbose(LOG_PREFIX, `the number of devices exceeds ${GCMRegistrationTokensMax}`);
|
|
42
|
-
// Make 1 send per slice
|
|
43
|
-
const promises = slices.reduce((memo, slice) => {
|
|
44
|
-
const promise = this.send(data, slice, timestamp);
|
|
45
|
-
memo.push(promise);
|
|
46
|
-
return memo;
|
|
47
|
-
}, [])
|
|
48
|
-
return Promise.all(promises).then((results) => {
|
|
49
|
-
const allResults = results.reduce((memo, result) => {
|
|
50
|
-
return memo.concat(result);
|
|
51
|
-
}, []);
|
|
52
|
-
return Promise.resolve(allResults);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
// get the devices back...
|
|
56
|
-
devices = slices[0];
|
|
57
|
-
|
|
58
|
-
let expirationTime;
|
|
59
|
-
// We handle the expiration_time convertion in push.js, so expiration_time is a valid date
|
|
60
|
-
// in Unix epoch time in milliseconds here
|
|
61
|
-
if (data['expiration_time']) {
|
|
62
|
-
expirationTime = data['expiration_time'];
|
|
63
|
-
}
|
|
64
|
-
// Generate gcm payload
|
|
65
|
-
// PushId is not a formal field of GCM, but Parse Android SDK uses this field to deduplicate push notifications
|
|
66
|
-
const gcmPayload = generateGCMPayload(data, pushId, timestamp, expirationTime);
|
|
67
|
-
// Make and send gcm request
|
|
68
|
-
const message = new gcm.Message(gcmPayload);
|
|
69
|
-
|
|
70
|
-
// Build a device map
|
|
71
|
-
const devicesMap = devices.reduce((memo, device) => {
|
|
72
|
-
memo[device.deviceToken] = device;
|
|
73
|
-
return memo;
|
|
74
|
-
}, {});
|
|
75
|
-
|
|
76
|
-
const deviceTokens = Object.keys(devicesMap);
|
|
77
|
-
|
|
78
|
-
const resolvers = [];
|
|
79
|
-
const promises = deviceTokens.map(() => new Promise(resolve => resolvers.push(resolve)));
|
|
80
|
-
const registrationTokens = deviceTokens;
|
|
81
|
-
const length = registrationTokens.length;
|
|
82
|
-
log.verbose(LOG_PREFIX, `sending to ${length} ${length > 1 ? 'devices' : 'device'}`);
|
|
83
|
-
this.sender.send(message, { registrationTokens: registrationTokens }, 5, (error, response) => {
|
|
84
|
-
// example response:
|
|
85
|
-
/*
|
|
86
|
-
{ "multicast_id":7680139367771848000,
|
|
87
|
-
"success":0,
|
|
88
|
-
"failure":4,
|
|
89
|
-
"canonical_ids":0,
|
|
90
|
-
"results":[ {"error":"InvalidRegistration"},
|
|
91
|
-
{"error":"InvalidRegistration"},
|
|
92
|
-
{"error":"InvalidRegistration"},
|
|
93
|
-
{"error":"InvalidRegistration"}] }
|
|
94
|
-
*/
|
|
95
|
-
if (error) {
|
|
96
|
-
log.error(LOG_PREFIX, `send errored: %s`, JSON.stringify(error, null, 4));
|
|
97
|
-
} else {
|
|
98
|
-
log.verbose(LOG_PREFIX, `GCM Response: %s`, JSON.stringify(response, null, 4));
|
|
99
|
-
}
|
|
100
|
-
const { results, multicast_id } = response || {};
|
|
101
|
-
registrationTokens.forEach((token, index) => {
|
|
102
|
-
const resolve = resolvers[index];
|
|
103
|
-
const result = results ? results[index] : undefined;
|
|
104
|
-
const device = devicesMap[token];
|
|
105
|
-
device.deviceType = 'android';
|
|
106
|
-
const resolution = {
|
|
107
|
-
device,
|
|
108
|
-
multicast_id,
|
|
109
|
-
response: error || result,
|
|
110
|
-
};
|
|
111
|
-
if (!result || result.error) {
|
|
112
|
-
resolution.transmitted = false;
|
|
113
|
-
} else {
|
|
114
|
-
resolution.transmitted = true;
|
|
115
|
-
}
|
|
116
|
-
resolve(resolution);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
return Promise.all(promises);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Generate the gcm payload from the data we get from api request.
|
|
124
|
-
* @param {Object} requestData The request body
|
|
125
|
-
* @param {String} pushId A random string
|
|
126
|
-
* @param {Number} timeStamp A number whose format is the Unix Epoch
|
|
127
|
-
* @param {Number|undefined} expirationTime A number whose format is the Unix Epoch or undefined
|
|
128
|
-
* @returns {Object} A promise which is resolved after we get results from gcm
|
|
129
|
-
*/
|
|
130
|
-
function generateGCMPayload(requestData, pushId, timeStamp, expirationTime) {
|
|
131
|
-
const payload = {
|
|
132
|
-
priority: 'high'
|
|
133
|
-
};
|
|
134
|
-
payload.data = {
|
|
135
|
-
data: requestData.data,
|
|
136
|
-
push_id: pushId,
|
|
137
|
-
time: new Date(timeStamp).toISOString()
|
|
138
|
-
}
|
|
139
|
-
const optionalKeys = ['contentAvailable', 'notification'];
|
|
140
|
-
optionalKeys.forEach((key) => {
|
|
141
|
-
if (requestData.hasOwnProperty(key)) {
|
|
142
|
-
payload[key] = requestData[key];
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
if (expirationTime) {
|
|
147
|
-
// The timeStamp and expiration is in milliseconds but gcm requires second
|
|
148
|
-
let timeToLive = Math.floor((expirationTime - timeStamp) / 1000);
|
|
149
|
-
if (timeToLive < 0) {
|
|
150
|
-
timeToLive = 0;
|
|
151
|
-
}
|
|
152
|
-
if (timeToLive >= GCMTimeToLiveMax) {
|
|
153
|
-
timeToLive = GCMTimeToLiveMax;
|
|
154
|
-
}
|
|
155
|
-
payload.timeToLive = timeToLive;
|
|
156
|
-
}
|
|
157
|
-
return payload;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Slice a list of devices to several list of devices with fixed chunk size.
|
|
162
|
-
* @param {Array} devices An array of devices
|
|
163
|
-
* @param {Number} chunkSize The size of the a chunk
|
|
164
|
-
* @returns {Array} An array which contaisn several arries of devices with fixed chunk size
|
|
165
|
-
*/
|
|
166
|
-
function sliceDevices(devices, chunkSize) {
|
|
167
|
-
const chunkDevices = [];
|
|
168
|
-
while (devices.length > 0) {
|
|
169
|
-
chunkDevices.push(devices.splice(0, chunkSize));
|
|
170
|
-
}
|
|
171
|
-
return chunkDevices;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
GCM.generateGCMPayload = generateGCMPayload;
|
|
175
|
-
|
|
176
|
-
/* istanbul ignore else */
|
|
177
|
-
if (process.env.TESTING) {
|
|
178
|
-
GCM.sliceDevices = sliceDevices;
|
|
179
|
-
}
|