@parse/push-adapter 6.11.0 → 7.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parse/push-adapter",
3
- "version": "6.11.0",
3
+ "version": "7.0.0",
4
4
  "description": "Base parse-server-push-adapter",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -25,27 +25,26 @@
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@parse/node-apn": "6.5.0",
28
- "@parse/node-gcm": "1.0.2",
29
- "expo-server-sdk": "3.14.0",
30
- "firebase-admin": "13.2.0",
28
+ "expo-server-sdk": "3.15.0",
29
+ "firebase-admin": "13.5.0",
31
30
  "npmlog": "7.0.1",
32
- "parse": "6.0.0",
31
+ "parse": "6.1.1",
33
32
  "web-push": "3.6.7"
34
33
  },
35
34
  "devDependencies": {
36
- "@eslint/js": "9.21.0",
35
+ "@eslint/js": "9.37.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": "11.0.1",
41
- "@semantic-release/npm": "12.0.1",
42
- "@semantic-release/release-notes-generator": "14.0.3",
39
+ "@semantic-release/github": "11.0.6",
40
+ "@semantic-release/npm": "12.0.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.22.0",
46
- "jasmine": "5.6.0",
44
+ "eslint": "9.37.0",
45
+ "jasmine": "5.12.0",
47
46
  "jasmine-spec-reporter": "7.0.0",
48
- "semantic-release": "24.2.3"
47
+ "semantic-release": "24.2.9"
49
48
  },
50
49
  "engines": {
51
50
  "node": "18 || 20 || 22"
@@ -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
- this.senderMap[pushType] = new GCM(pushConfig[pushType]);
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 [].concat.apply([], promises);
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, GCM, WEB, EXPO, utils };
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
- }