Changeset View
Changeset View
Standalone View
Standalone View
keyserver/src/push/utils.js
Show All 18 Lines | |||||
import { | import { | ||||
getAPNPushProfileForCodeVersion, | getAPNPushProfileForCodeVersion, | ||||
getFCMPushProfileForCodeVersion, | getFCMPushProfileForCodeVersion, | ||||
getAPNProvider, | getAPNProvider, | ||||
getFCMProvider, | getFCMProvider, | ||||
ensureWebPushInitialized, | ensureWebPushInitialized, | ||||
getWNSToken, | getWNSToken, | ||||
} from './providers.js'; | } from './providers.js'; | ||||
import type { AndroidNotification } from './types.js'; | |||||
import { dbQuery, SQL } from '../database/database.js'; | import { dbQuery, SQL } from '../database/database.js'; | ||||
const fcmTokenInvalidationErrors = new Set([ | const fcmTokenInvalidationErrors = new Set([ | ||||
'messaging/registration-token-not-registered', | 'messaging/registration-token-not-registered', | ||||
'messaging/invalid-registration-token', | 'messaging/invalid-registration-token', | ||||
]); | ]); | ||||
const fcmMaxNotificationPayloadByteSize = 4000; | const fcmMaxNotificationPayloadByteSize = 4000; | ||||
const apnTokenInvalidationErrorCode = 410; | const apnTokenInvalidationErrorCode = 410; | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
type FCMPushResult = { | type FCMPushResult = { | ||||
+success?: true, | +success?: true, | ||||
+fcmIDs?: $ReadOnlyArray<string>, | +fcmIDs?: $ReadOnlyArray<string>, | ||||
+errors?: $ReadOnlyArray<FirebaseError>, | +errors?: $ReadOnlyArray<FirebaseError>, | ||||
+invalidTokens?: $ReadOnlyArray<string>, | +invalidTokens?: $ReadOnlyArray<string>, | ||||
}; | }; | ||||
async function fcmPush({ | async function fcmPush({ | ||||
notification, | notifications, | ||||
deviceTokens, | deviceTokens, | ||||
collapseKey, | collapseKey, | ||||
codeVersion, | codeVersion, | ||||
}: { | }: { | ||||
+notification: Object, | +notifications: $ReadOnlyArray<AndroidNotification>, | ||||
+deviceTokens: $ReadOnlyArray<string>, | +deviceTokens: $ReadOnlyArray<string>, | ||||
+codeVersion: ?number, | +codeVersion: ?number, | ||||
+collapseKey?: ?string, | +collapseKey?: ?string, | ||||
}): Promise<FCMPushResult> { | }): Promise<FCMPushResult> { | ||||
const pushProfile = getFCMPushProfileForCodeVersion(codeVersion); | const pushProfile = getFCMPushProfileForCodeVersion(codeVersion); | ||||
const fcmProvider = await getFCMProvider(pushProfile); | const fcmProvider = await getFCMProvider(pushProfile); | ||||
if (!fcmProvider && process.env.NODE_ENV === 'development') { | if (!fcmProvider && process.env.NODE_ENV === 'development') { | ||||
console.log(`no keyserver/secrets/${pushProfile}.json so ignoring notifs`); | console.log(`no keyserver/secrets/${pushProfile}.json so ignoring notifs`); | ||||
return { success: true }; | return { success: true }; | ||||
} | } | ||||
invariant(fcmProvider, `keyserver/secrets/${pushProfile}.json should exist`); | invariant(fcmProvider, `keyserver/secrets/${pushProfile}.json should exist`); | ||||
const options: Object = { | const options: Object = { | ||||
priority: 'high', | priority: 'high', | ||||
}; | }; | ||||
if (collapseKey) { | if (collapseKey) { | ||||
options.collapseKey = collapseKey; | options.collapseKey = collapseKey; | ||||
} | } | ||||
// firebase-admin is extremely barebones and has a lot of missing or poorly | // firebase-admin is extremely barebones and has a lot of missing or poorly | ||||
// thought-out functionality. One of the issues is that if you send a | // thought-out functionality. One of the issues is that if you send a | ||||
// multicast messages and one of the device tokens is invalid, the resultant | // 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 | // 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. | // avoid the multicast functionality and call it once per deviceToken. | ||||
const promises = []; | const promises = []; | ||||
for (const deviceToken of deviceTokens) { | for (const [idx, deviceToken] of deviceTokens.entries()) { | ||||
const notification = | |||||
idx < notifications.length ? notifications[idx] : notifications[0]; | |||||
promises.push( | promises.push( | ||||
fcmSinglePush(fcmProvider, notification, deviceToken, options), | fcmSinglePush(fcmProvider, notification, deviceToken, options), | ||||
); | ); | ||||
} | } | ||||
tomek: Is it guaranteed that device tokes match notifications? Can we make the API safer by merging… | |||||
marcinAuthorUnsubmitted Done Inline Actions
It is guaranted since:
I think we can marcin: > Is it guaranteed that device tokes match notifications?
It is guaranted since:
1. Function… | |||||
const pushResults = await Promise.all(promises); | const pushResults = await Promise.all(promises); | ||||
const errors = []; | const errors = []; | ||||
const ids = []; | const ids = []; | ||||
const invalidTokens = []; | const invalidTokens = []; | ||||
for (let i = 0; i < pushResults.length; i++) { | for (let i = 0; i < pushResults.length; i++) { | ||||
const pushResult = pushResults[i]; | const pushResult = pushResults[i]; | ||||
for (const error of pushResult.errors) { | for (const error of pushResult.errors) { | ||||
▲ Show 20 Lines • Show All 239 Lines • Show Last 20 Lines |
Is it guaranteed that device tokes match notifications? Can we make the API safer by merging these into one object with deviceToken and notification fields? (and add an array of these as a parameter)