diff --git a/keyserver/src/push/utils.js b/keyserver/src/push/utils.js --- a/keyserver/src/push/utils.js +++ b/keyserver/src/push/utils.js @@ -70,16 +70,13 @@ }), ); - const mergedResults = { sent: [], failed: [] }; + const errors: Array = []; for (const result of results) { - mergedResults.sent.push(...result.sent); - mergedResults.failed.push(...result.failed); + errors.push(...result.failed); } - const errors = []; - const invalidTokens = []; - for (const error of mergedResults.failed) { - errors.push(error); + const invalidTokens: Array = []; + for (const error of errors) { /* eslint-disable eqeqeq */ if ( error.status == apnTokenInvalidationErrorCode || @@ -99,12 +96,13 @@ } } -type FCMPushResult = { - +success?: true, - +fcmIDs?: $ReadOnlyArray, - +errors?: $ReadOnlyArray, - +invalidTokens?: $ReadOnlyArray, +type WritableFCMPushResult = { + success?: true, + fcmIDs?: $ReadOnlyArray, + errors?: $ReadOnlyArray, + invalidTokens?: $ReadOnlyArray, }; +export type FCMPushResult = $ReadOnly; async function fcmPush({ targetedNotifications, collapseKey, @@ -132,22 +130,24 @@ // multicast messages and one of the device tokens is invalid, the resultant // won't explain which of the device tokens is invalid. So we're forced to // avoid the multicast functionality and call it once per deviceToken. - const promises = []; - for (const { notification, deviceToken } of targetedNotifications) { - promises.push( - fcmSinglePush(fcmProvider, notification, deviceToken, options), - ); - } - const pushResults = await Promise.all(promises); + const results = await Promise.all( + targetedNotifications.map(({ notification, deviceToken }) => { + return fcmSinglePush(fcmProvider, notification, deviceToken, options); + }), + ); const errors = []; const ids = []; const invalidTokens = []; - for (let i = 0; i < pushResults.length; i++) { - const pushResult = pushResults[i]; + for (let i = 0; i < results.length; i++) { + const pushResult = results[i]; for (const error of pushResult.errors) { - errors.push(error); - if (fcmTokenInvalidationErrors.has(error.errorInfo.code)) { + errors.push(error.error); + const errorCode = + error.type === 'firebase_error' + ? error.error.errorInfo.code + : undefined; + if (errorCode && fcmTokenInvalidationErrors.has(errorCode)) { invalidTokens.push(targetedNotifications[i].deviceToken); } } @@ -156,7 +156,7 @@ } } - const result = {}; + const result: WritableFCMPushResult = {}; if (ids.length > 0) { result.fcmIDs = ids; } @@ -168,15 +168,22 @@ if (invalidTokens.length > 0) { result.invalidTokens = invalidTokens; } - return { ...result }; + return result; } +type FCMSinglePushError = + | { +type: 'firebase_error', +error: FirebaseError } + | { +type: 'exception', +error: mixed }; +type FCMSinglePushResult = { + +fcmIDs: $ReadOnlyArray, + +errors: $ReadOnlyArray, +}; async function fcmSinglePush( provider: FirebaseApp, notification: Object, deviceToken: string, options: Object, -) { +): Promise { try { const deliveryResult = await provider .messaging() @@ -185,14 +192,14 @@ const ids = []; for (const fcmResult of deliveryResult.results) { if (fcmResult.error) { - errors.push(fcmResult.error); + errors.push({ type: 'firebase_error', error: fcmResult.error }); } else if (fcmResult.messageId) { ids.push(fcmResult.messageId); } } return { fcmIDs: ids, errors }; } catch (e) { - return { fcmIDs: [], errors: [e] }; + return { fcmIDs: [], errors: [{ type: 'exception', error: e }] }; } } @@ -211,7 +218,7 @@ GROUP BY user `; const [result] = await dbQuery(query); - const usersToUnreadCounts = {}; + const usersToUnreadCounts: { [string]: number } = {}; for (const row of result) { usersToUnreadCounts[row.user.toString()] = row.unread_count; } @@ -228,17 +235,21 @@ +headers: { +[string]: string }, +body: string, }; -type WebPushResult = { - +success?: true, - +errors?: $ReadOnlyArray, - +invalidTokens?: $ReadOnlyArray, +type WritableWebPushResult = { + success?: true, + errors?: $ReadOnlyArray, + invalidTokens?: $ReadOnlyArray, +}; +type WebPushResult = $ReadOnly; +type WebPushAttempt = { + +error?: WebPushError, }; async function webPush( targetedNotifications: $ReadOnlyArray, ): Promise { await ensureWebPushInitialized(); - const pushResults = await Promise.all( + const pushResults: $ReadOnlyArray = await Promise.all( targetedNotifications.map( async ({ notification, deviceToken: deviceTokenString }) => { const deviceToken: PushSubscriptionJSON = JSON.parse(deviceTokenString); @@ -246,7 +257,7 @@ try { await webpush.sendNotification(deviceToken, notificationString); } catch (error) { - return { error }; + return ({ error }: WebPushAttempt); } return {}; }, @@ -260,15 +271,16 @@ ); for (let i = 0; i < pushResults.length; i++) { const pushResult = pushResults[i]; - if (pushResult.error) { - errors.push(pushResult.error); - if (webInvalidTokenErrorCodes.includes(pushResult.error.statusCode)) { + const { error } = pushResult; + if (error) { + errors.push(error); + if (webInvalidTokenErrorCodes.includes(error.statusCode)) { invalidTokens.push(deviceTokens[i]); } } } - const result = {}; + const result: WritableWebPushResult = {}; if (errors.length > 0) { result.errors = errors; } else { @@ -277,16 +289,17 @@ if (invalidTokens.length > 0) { result.invalidTokens = invalidTokens; } - return { ...result }; + return result; } export type WNSPushError = any | string | Response; -type WNSPushResult = { - +success?: true, - +wnsIDs?: $ReadOnlyArray, - +errors?: $ReadOnlyArray, - +invalidTokens?: $ReadOnlyArray, +type WritableWNSPushResult = { + success?: true, + wnsIDs?: $ReadOnlyArray, + errors?: $ReadOnlyArray, + invalidTokens?: $ReadOnlyArray, }; +type WNSPushResult = $ReadOnly; async function wnsPush( targetedNotifications: $ReadOnlyArray, ): Promise { @@ -334,7 +347,7 @@ } } - const result = {}; + const result: WritableWNSPushResult = {}; if (notifIDs.length > 0) { result.wnsIDs = notifIDs; } @@ -346,7 +359,7 @@ if (invalidTokens.length > 0) { result.invalidTokens = invalidTokens; } - return { ...result }; + return result; } async function wnsSinglePush(token: string, notification: string, url: string) {