diff --git a/.babelrc b/.babelrc index 2547b5b..7be671b 100644 --- a/.babelrc +++ b/.babelrc @@ -4,7 +4,7 @@ "@babel/preset-env", { "targets": { - "node": "6" + "node": "14" } } ] diff --git a/.eslintrc b/.eslintrc index 3f03e1f..06c95b8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,11 @@ { "extends": ["airbnb-base", "plugin:prettier/recommended"], "env": { - "node": true + "node": true, + "es2020": true + }, + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" } } diff --git a/package-lock.json b/package-lock.json index 28bc805..9aa364a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "firebase-admin": "12.1.1", "node-adm": "0.9.1", "node-gcm": "1.1.4", - "ramda": "0.32.0", "web-push": "3.6.7", "wns": "0.5.4" }, @@ -9802,16 +9801,6 @@ } ] }, - "node_modules/ramda": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.32.0.tgz", - "integrity": "sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ramda" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -18934,11 +18923,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "ramda": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.32.0.tgz", - "integrity": "sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==" - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index 8852d8d..58eee4e 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,6 @@ "firebase-admin": "12.1.1", "node-adm": "0.9.1", "node-gcm": "1.1.4", - "ramda": "0.32.0", "web-push": "3.6.7", "wns": "0.5.4" }, diff --git a/src/push-notifications.js b/src/push-notifications.js index 79b8150..91c292b 100644 --- a/src/push-notifications.js +++ b/src/push-notifications.js @@ -37,11 +37,11 @@ class PN { sendWith(method, regIds, data, cb) { return method(regIds, data, this.settings) .then((results) => { - (cb || ((noop) => noop))(null, results); + cb?.(null, results); return results; }) .catch((error) => { - (cb || ((noop) => noop))(error); + cb?.(error); return Promise.reject(error); }); } diff --git a/src/sendADM.js b/src/sendADM.js index 08a0f78..201241a 100644 --- a/src/sendADM.js +++ b/src/sendADM.js @@ -10,13 +10,7 @@ const sendADM = (regIds, _data, settings) => { }; const promises = []; const admSender = new adm.Sender(settings.adm); - const data = { ..._data }; - const { consolidationKey, expiry, timeToLive, custom } = data; - - delete data.consolidationKey; - delete data.expiry; - delete data.timeToLive; - delete data.custom; + const { consolidationKey, expiry, timeToLive, custom, ...data } = _data; const message = { expiresAfter: diff --git a/src/sendAPN.js b/src/sendAPN.js index a7e99bd..841d113 100644 --- a/src/sendAPN.js +++ b/src/sendAPN.js @@ -1,13 +1,8 @@ const apn = require('@parse/node-apn'); -const R = require('ramda'); const { APN_METHOD } = require('./constants'); const { buildApnsMessage } = require('./utils/tools'); -const getDeviceTokenOrSelf = R.ifElse( - R.has('device'), - R.prop('device'), - R.identity -); +const getDeviceTokenOrSelf = (token) => token?.device ?? token; class APN { constructor(settings) { diff --git a/src/sendFCM.js b/src/sendFCM.js index cbde674..e31b645 100644 --- a/src/sendFCM.js +++ b/src/sendFCM.js @@ -4,21 +4,8 @@ const { FCM_METHOD } = require('./constants'); const FcmMessage = require('./utils/fcmMessage'); const { containsValidRecipients } = require('./utils/tools'); -const getRecipientList = (obj) => { - if (obj.tokens) { - return obj.tokens; - } - if (obj.token) { - return [obj.token]; - } - if (obj.condition) { - return [obj.condition]; - } - if (obj.topic) { - return [obj.topic]; - } - return []; -}; +const getRecipientList = (obj) => + obj.tokens ?? [obj.token, obj.condition, obj.topic].filter(Boolean); const sendChunk = (firebaseApp, recipients, message) => { const firebaseMessage = message.buildWithRecipients(recipients); @@ -50,12 +37,15 @@ const sendChunk = (firebaseApp, recipients, message) => { message: response.responses.map((value) => { const regToken = recipientList[regIndex]; regIndex += 1; + const errorMsg = value.error + ? value.error.message || value.error + : null; return { messageId: value.message_id, originalRegId: regToken, regId: value.registration_id || regToken, error: value.error ? new Error(value.error) : null, - errorMsg: value.error ? value.error.message || value.error : null, + errorMsg, }; }), }; diff --git a/src/sendGCM.js b/src/sendGCM.js index c6bdf36..610c00b 100644 --- a/src/sendGCM.js +++ b/src/sendGCM.js @@ -2,18 +2,8 @@ const gcm = require('node-gcm'); const { GCM_METHOD } = require('./constants'); const { containsValidRecipients, buildGcmMessage } = require('./utils/tools'); -const getRecipientList = (obj) => { - if (obj.registrationTokens) { - return obj.registrationTokens; - } - if (obj.to) { - return [obj.to]; - } - if (obj.condition) { - return [obj.condition]; - } - return []; -}; +const getRecipientList = (obj) => + obj.registrationTokens ?? [obj.to, obj.condition].filter(Boolean); const sendChunk = (GCMSender, recipients, message, retries) => new Promise((resolve) => { @@ -43,12 +33,15 @@ const sendChunk = (GCMSender, recipients, message, retries) => message: response.results.map((value) => { const regToken = recipientList[regIndex]; regIndex += 1; + const errorMsg = value.error + ? value.error.message || value.error + : null; return { messageId: value.message_id, originalRegId: regToken, regId: value.registration_id || regToken, error: value.error ? new Error(value.error) : null, - errorMsg: value.error ? value.error.message || value.error : null, + errorMsg, }; }), }); diff --git a/src/sendWNS.js b/src/sendWNS.js index afcc8f3..0d88ee6 100644 --- a/src/sendWNS.js +++ b/src/sendWNS.js @@ -3,12 +3,8 @@ const { WNS_METHOD } = require('./constants'); const parseErrorMessage = (err) => (err instanceof Error ? err.message : err); const parseError = (err) => { - if (err instanceof Error) { - return err; - } - if (err) { - return new Error(err); - } + if (err instanceof Error) return err; + if (err) return new Error(err); return null; }; diff --git a/src/sendWeb.js b/src/sendWeb.js index c4220c6..fb3f35b 100644 --- a/src/sendWeb.js +++ b/src/sendWeb.js @@ -1,8 +1,8 @@ const webPush = require('web-push'); -const { is, unless, assoc } = require('ramda'); const { WEB_METHOD } = require('./constants'); -const stringify = unless(is(String), JSON.stringify); +const stringify = (data) => + typeof data === 'string' ? data : JSON.stringify(data); const sendWebPush = async (regIds, data, settings) => { const payload = stringify(data); @@ -39,7 +39,7 @@ const sendWebPush = async (regIds, data, settings) => { failure: acc.failure + current.failure, message: [...acc.message, ...current.message], })); - return assoc('method', WEB_METHOD, reduced); + return { ...reduced, method: WEB_METHOD }; }; module.exports = sendWebPush; diff --git a/src/utils/fcmMessage.js b/src/utils/fcmMessage.js index 39dc1ea..e9907f7 100644 --- a/src/utils/fcmMessage.js +++ b/src/utils/fcmMessage.js @@ -20,16 +20,10 @@ class FcmMessage { static normalizeDataParams(data) { if (!data) return {}; return Object.entries(data).reduce((normalized, [key, value]) => { - if (value === undefined || value === null) { - return normalized; - } - + if (value == null) return normalized; const stringifyValue = typeof value === 'string' ? value : JSON.stringify(value); - - Object.assign(normalized, { [key]: stringifyValue }); - - return normalized; + return { ...normalized, [key]: stringifyValue }; }, {}); } diff --git a/src/utils/tools.js b/src/utils/tools.js index b7936a0..b5a06a8 100644 --- a/src/utils/tools.js +++ b/src/utils/tools.js @@ -1,50 +1,53 @@ -const R = require('ramda'); const { Notification: ApnsMessage } = require('@parse/node-apn'); const { Message: GcmMessage } = require('node-gcm'); const { DEFAULT_TTL, GCM_MAX_TTL } = require('../constants'); -const ttlFromExpiry = R.compose( - R.min(GCM_MAX_TTL), - R.max(0), - (expiry) => expiry - Math.floor(Date.now() / 1000) -); +const ttlFromExpiry = (expiry) => { + const ttl = expiry - Math.floor(Date.now() / 1000); + return Math.min(Math.max(ttl, 0), GCM_MAX_TTL); +}; -const extractTimeToLive = R.cond([ - [R.propIs(Number, 'expiry'), ({ expiry }) => ttlFromExpiry(expiry)], - [R.propIs(Number, 'timeToLive'), R.prop('timeToLive')], - [R.T, R.always(DEFAULT_TTL)], -]); +const extractTimeToLive = (data) => { + if (typeof data?.expiry === 'number') return ttlFromExpiry(data.expiry); + if (typeof data?.timeToLive === 'number') return data.timeToLive; + return DEFAULT_TTL; +}; const expiryFromTtl = (ttl) => ttl + Math.floor(Date.now() / 1000); -const extractExpiry = R.cond([ - [R.propIs(Number, 'expiry'), R.prop('expiry')], - [ - R.propIs(Number, 'timeToLive'), - ({ timeToLive }) => expiryFromTtl(timeToLive), - ], - [R.T, () => expiryFromTtl(DEFAULT_TTL)], -]); - -const getPropValueOrUndefinedIfIsSilent = (propName, data) => { - if (data.silent) { +const extractExpiry = (data) => { + if (typeof data?.expiry === 'number') return data.expiry; + if (typeof data?.timeToLive === 'number') + return expiryFromTtl(data.timeToLive); + return expiryFromTtl(DEFAULT_TTL); +}; + +const getPropValueOrUndefinedIfIsSilent = (propName, data) => + data.silent ? undefined : data[propName]; + +const toJSONorUndefined = (value) => { + if (typeof value !== 'string') { + return value; + } + try { + return JSON.parse(value); + } catch (e) { return undefined; } - return data[propName]; }; -const toJSONorUndefined = R.when( - R.is(String), - R.tryCatch(JSON.parse, R.always(undefined)) -); - -const alertLocArgsToJSON = R.evolve({ - alert: { - 'title-loc-args': toJSONorUndefined, - 'loc-args': toJSONorUndefined, - }, -}); +const alertLocArgsToJSON = (data) => { + const alert = data.alert ?? {}; + return { + ...data, + alert: { + ...alert, + 'title-loc-args': toJSONorUndefined(alert['title-loc-args']), + 'loc-args': toJSONorUndefined(alert['loc-args']), + }, + }; +}; const getDefaultAlert = (data) => ({ title: data.title, @@ -57,21 +60,31 @@ const getDefaultAlert = (data) => ({ action: data.action, }); -const alertOrDefault = (data) => - R.when( - R.propSatisfies(R.isNil, 'alert'), - R.assoc('alert', getDefaultAlert(data)) - ); +const alertOrDefault = (data) => ({ + ...data, + alert: data.alert ?? getDefaultAlert(data), +}); -const getParsedAlertOrDefault = (data) => - R.pipe(alertOrDefault(data), alertLocArgsToJSON)(data); +const getParsedAlertOrDefault = (data) => { + const withAlert = alertOrDefault(data); + return alertLocArgsToJSON(withAlert); +}; -const pathIsString = R.pathSatisfies(R.is(String)); +const pathIsString = (path) => (val) => { + const current = path.reduce((acc, key) => { + if (acc && typeof acc === 'object') { + return acc[key]; + } + return null; + }, val); + return typeof current === 'string'; +}; -const containsValidRecipients = R.either( - pathIsString(['recipients', 'to']), - pathIsString(['recipients', 'condition']) -); +const containsValidRecipients = (obj) => { + const checkTo = pathIsString(['recipients', 'to'])(obj); + const checkCondition = pathIsString(['recipients', 'condition'])(obj); + return checkTo || checkCondition; +}; const buildGcmNotification = (data) => { const notification = data.fcm_notification || {