From 1ec56cd1abc3c1a09d62af74f2a407618b311370 Mon Sep 17 00:00:00 2001 From: Cesar <26280794+csrapr@users.noreply.github.com> Date: Thu, 10 Dec 2020 18:26:13 +0000 Subject: [PATCH] Feat: crypto balance notifications saving in DB Chore: add new column "detail" to transactions table migration Feat: check if older notification is valid before sending new one Feat: error saving to database Fix: fix error when invalidating notification on clearCryptoBalanceNotifications Chre: code refactor in new-settings-loader for simplicity Chore: refactor code on notifier and merge similar functions --- lib/notifier/email.js | 4 - lib/notifier/index.js | 129 +++++++++++++++++- lib/notifier/sms.js | 7 +- lib/notifier/utils.js | 14 +- lib/poller.js | 2 +- .../sections/CryptoBalanceOverrides.js | 4 +- 6 files changed, 138 insertions(+), 22 deletions(-) diff --git a/lib/notifier/email.js b/lib/notifier/email.js index 174b17ec..289180c0 100644 --- a/lib/notifier/email.js +++ b/lib/notifier/email.js @@ -84,8 +84,4 @@ function emailAlert (alert) { const sendMessage = email.sendMessage -<<<<<<< HEAD -======= - ->>>>>>> 1706b2c... Feat: save highVolumeTxs on DB and plugins code refactor module.exports = { alertSubject, printEmailAlerts, sendMessage } diff --git a/lib/notifier/index.js b/lib/notifier/index.js index e70ea746..792e4b6c 100644 --- a/lib/notifier/index.js +++ b/lib/notifier/index.js @@ -371,7 +371,7 @@ const cashCassettesNotify = (cassettes, deviceId) => { if(cashOutEnabled) { // we only want to add this notification if there isn't one already set and unread in the database - Promise.all([queries.getUnreadCassetteNotifications(1), queries.getUnreadCassetteNotifications(2)]).then(res => { + Promise.all([queries.getUnreadCassetteNotifications(1, deviceId), queries.getUnreadCassetteNotifications(2, deviceId)]).then(res => { if(res[0].length === 0 && cassette1Count < cassette1Threshold) { console.log("Adding fiatBalance alert for cashbox 1 in database - count & threshold: ", cassette1Count, cassette1Threshold ) queries.addCashCassetteWarning(1, deviceId) @@ -385,6 +385,133 @@ const cashCassettesNotify = (cassettes, deviceId) => { }) } + +/* + Notes for new "valid" column on notifications table: + + - We only want to add a new notification if it is present in the high or low warning consts. + - Before we add the notification we need to see if there is no "valid" notification in the database. + - Since the poller runs every few seconds, if the user marks it as read, this code would add a new notification + immediately. This new column helps us decide if a new notification should be added. + - "Valid" is defaulted to "true". When the cryptobalance goes over the low threshold or under the high threshold, + the notification will be marked as invalid. This will allow a new one to be sent. If the cryptobalance never goes + into the middle of the high and low thresholds, the old, "read" notification will still be relevant so we won't add + a new one. +*/ + +const clearOldCryptoNotifications = (balances) => { + // get valid crypto notifications from DB + // if that notification doesn't exist in balances, then make it invalid on the DB + queries.getAllValidNotifications('cryptoBalance').then(res => { + const notifications = _.map(it => { + return { + cryptoCode: it.detail.split('_')[0], + code: it.detail.split('_').splice(1).join('_') + } + }, res) + _.forEach(notification => { + const idx = _.findIndex(balance => { + return balance.code === notification.code && balance.cryptoCode === notification.cryptoCode + }, balances) + + if(idx !== -1) { + return + } + // if the notification doesn't exist in the new balances object, then it is outdated and is not valid anymore + queries.invalidateNotification(notification.id) + }, notifications) + }) +} + +const balancesNotify = (balances) => { + const highFilter = o => o.code === 'HIGH_CRYPTO_BALANCE' + const lowFilter = o => o.code === 'LOW_CRYPTO_BALANCE' + const highWarnings = _.filter(highFilter, balances) + const lowWarnings = _.filter(lowFilter, balances) + + clearOldCryptoNotifications(balances) + + highWarnings.forEach(warning => { + queries.getValidNotifications('cryptoBalance', `${warning.cryptoCode}_${warning.code}`).then(res => { + if (res.length > 0) { + return Promise.resolve() + } + console.log("Adding high balance alert for " + warning.cryptoCode + " - " + warning.fiatBalance.balance) + const balance = utils.formatCurrency(warning.fiatBalance.balance, warning.fiatCode) + queries.addCryptoBalanceWarning(`${warning.cryptoCode}_${warning.code}`, `High balance in ${warning.cryptoCode} [${balance}]`) + }) + }) + lowWarnings.forEach(warning => { + queries.getValidNotifications('cryptoBalance', `${warning.cryptoCode}_${warning.code}`).then(res => { + if (res.length > 0) { + return Promise.resolve() + } + console.log("Adding low balance alert for " + warning.cryptoCode + " - " + warning.fiatBalance.balance) + const balance = utils.formatCurrency(warning.fiatBalance.balance, warning.fiatCode) + queries.addCryptoBalanceWarning(`${warning.cryptoCode}_${warning.code}`, `Low balance in ${warning.cryptoCode} [${balance}]`) + }) + }) +} + +const clearOldErrorNotifications = (alerts) => { + queries.getAllValidNotifications('error').then(res => { + _.forEach(notification => { + const idx = _.findIndex(alert => { + return alert.code === notification.detail.split('_')[0] && alert.deviceId === notification.device_id + }, alerts) + if(idx !== -1) { + return + } + // if the notification doesn't exist, then it is outdated and is not valid anymore + queries.invalidateNotification(notification.id) + }, res) + }) +} + +const errorAlertsNotify = (alertRec) => { + let alerts = [] + _.keys(alertRec.devices).forEach(function (device) { + // embed device ID in each alert object inside the deviceAlerts array + alertRec.devices[device].deviceAlerts = _.map(alert => { + return {...alert, deviceId: device} + }, alertRec.devices[device].deviceAlerts) + // concat every array into one + alerts = _.concat(alerts, alertRec.devices[device].deviceAlerts) + }) + + // now that we have all the alerts, we want to add PING and STALE alerts to the DB + // if there is a valid alert on the DB that doesn't exist on the new alerts array, + // that alert should be considered invalid + // after that, for the alerts array, we have to see if there is a valid alert of + // the sorts already on the DB + clearOldErrorNotifications(alerts) + + _.forEach(alert => { + switch(alert.code) { + case PING: + return queries.getValidNotifications('error', PING, alert.deviceId).then(res => { + if(res.length > 0) { + return Promise.resolve() + } + console.log("Adding PING alert on database for " + alert.machineName) + const message = `Machine down` + queries.addErrorNotification(`${PING}_${alert.age ? alert.age : '-1'}`, message, alert.deviceId) + }) + case STALE: + return queries.getValidNotifications('error', STALE, alert.deviceId).then(res => { + if(res.length > 0) { + return Promise.resolve() + } + console.log("Adding STALE alert on database for " + alert.machineName) + const message = `Machine is stuck on ${alert.state} screen` + queries.addErrorNotification(STALE, message, alert.deviceId) + }) + default: + break + } + }, alerts) +} + module.exports = { transactionNotify, checkNotification, diff --git a/lib/notifier/sms.js b/lib/notifier/sms.js index 65a64251..592a8a45 100644 --- a/lib/notifier/sms.js +++ b/lib/notifier/sms.js @@ -21,7 +21,12 @@ function printSmsAlerts (alertRec, config) { const code = entry[0] const machineNames = _.filter( _.negate(_.isEmpty), - _.map('machineName', entry[1]) + _.map('machineName', entry[1]), + ) + + const cryptoCodes = _.filter( + _.negate(_.isEmpty), + _.map('cryptoCode', entry[1]), ) const cryptoCodes = _.filter( diff --git a/lib/notifier/utils.js b/lib/notifier/utils.js index 774712d4..29fb504f 100644 --- a/lib/notifier/utils.js +++ b/lib/notifier/utils.js @@ -108,13 +108,8 @@ const buildTransactionMessage = (tx, rec, highValueTx, machineName, customer) => status = !isCashOut ? 'Successful' : !rec.isRedemption -<<<<<<< HEAD ? 'Successful & awaiting redemption' : 'Successful & dispensed' -======= - ? 'Successful & awaiting redemption' - : 'Successful & dispensed' ->>>>>>> 1706b2c... Feat: save highVolumeTxs on DB and plugins code refactor } const body = ` @@ -141,7 +136,6 @@ const buildTransactionMessage = (tx, rec, highValueTx, machineName, customer) => }, highValueTx] } -<<<<<<< HEAD function formatCurrency (num, code) { return numeral(num).format('0,0.00') + ' ' + code } @@ -186,8 +180,6 @@ function getAlertTypes (alertRec, config) { return alerts } -======= ->>>>>>> 1706b2c... Feat: save highVolumeTxs on DB and plugins code refactor module.exports = { codeDisplay, parseEventNote, @@ -200,13 +192,9 @@ module.exports = { shouldNotAlert, buildAlertFingerprint, sendNoAlerts, -<<<<<<< HEAD buildTransactionMessage, formatCurrency, formatAge, buildDetail, - deviceAlerts -======= - buildTransactionMessage ->>>>>>> 1706b2c... Feat: save highVolumeTxs on DB and plugins code refactor + deviceAlerts, } diff --git a/lib/poller.js b/lib/poller.js index e03bd689..7c90871b 100644 --- a/lib/poller.js +++ b/lib/poller.js @@ -1,7 +1,7 @@ const _ = require('lodash/fp') const plugins = require('./plugins') -const notifier = require('./notifier') +const notifier = require('./notifier/index') const T = require('./time') const logger = require('./logger') const cashOutTx = require('./cash-out/cash-out-tx') diff --git a/new-lamassu-admin/src/pages/Notifications/sections/CryptoBalanceOverrides.js b/new-lamassu-admin/src/pages/Notifications/sections/CryptoBalanceOverrides.js index e8673efc..fe497913 100644 --- a/new-lamassu-admin/src/pages/Notifications/sections/CryptoBalanceOverrides.js +++ b/new-lamassu-admin/src/pages/Notifications/sections/CryptoBalanceOverrides.js @@ -9,8 +9,8 @@ import { transformNumber } from 'src/utils/number' import NotificationsCtx from '../NotificationsContext' -const HIGH_BALANCE_KEY = 'highBalance' -const LOW_BALANCE_KEY = 'lowBalance' +const HIGH_BALANCE_KEY = 'cryptoHighBalance' +const LOW_BALANCE_KEY = 'cryptoLowBalance' const CRYPTOCURRENCY_KEY = 'cryptoCurrency' const NAME = 'cryptoBalanceOverrides'