From 3480bbf8f7a9e1524ed4b42c0b93784eeb727a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Thu, 29 Jul 2021 16:16:31 +0100 Subject: [PATCH 1/6] feat: add custom SMS screen feat: custom messages database migration feat: populate custom SMS screen with database data feat: custom SMS creation modal fix: small fixes on styling --- lib/custom-sms.js | 17 +++ lib/new-admin/graphql/resolvers/index.js | 2 + .../graphql/resolvers/sms.resolver.js | 12 ++ lib/new-admin/graphql/types/index.js | 2 + lib/new-admin/graphql/types/sms.type.js | 25 ++++ migrations/1627518944902-custom-sms.js | 20 +++ .../pages/OperatorInfo/CustomSMS/CustomSMS.js | 127 ++++++++++++++++++ .../OperatorInfo/CustomSMS/CustomSMSModal.js | 73 ++++++++++ .../pages/OperatorInfo/OperatorInfo.styles.js | 7 + .../src/routing/lamassu.routes.js | 8 ++ new-lamassu-admin/src/routing/pazuz.routes.js | 8 ++ 11 files changed, 301 insertions(+) create mode 100644 lib/custom-sms.js create mode 100644 lib/new-admin/graphql/resolvers/sms.resolver.js create mode 100644 lib/new-admin/graphql/types/sms.type.js create mode 100644 migrations/1627518944902-custom-sms.js create mode 100644 new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js create mode 100644 new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js diff --git a/lib/custom-sms.js b/lib/custom-sms.js new file mode 100644 index 00000000..3ba6d9c0 --- /dev/null +++ b/lib/custom-sms.js @@ -0,0 +1,17 @@ +const uuid = require('uuid') +const db = require('./db') + +const getCustomMessages = () => { + const sql = `SELECT * FROM custom_messages` + return db.any(sql) +} + +const createCustomMessage = (event, deviceId, message) => { + const sql = `INSERT INTO custom_message (event, device_id, message) VALUES ($2, $3, $4)` + return db.none(sql, [uuid.v4(), event, deviceId, message]) +} + +module.exports = { + getCustomMessages, + createCustomMessage +} diff --git a/lib/new-admin/graphql/resolvers/index.js b/lib/new-admin/graphql/resolvers/index.js index 033593d7..f93c2bc5 100644 --- a/lib/new-admin/graphql/resolvers/index.js +++ b/lib/new-admin/graphql/resolvers/index.js @@ -15,6 +15,7 @@ const pairing = require('./pairing.resolver') const rates = require('./rates.resolver') const scalar = require('./scalar.resolver') const settings = require('./settings.resolver') +const sms = require('./sms.resolver') const status = require('./status.resolver') const transaction = require('./transaction.resolver') const user = require('./users.resolver') @@ -36,6 +37,7 @@ const resolvers = [ rates, scalar, settings, + sms, status, transaction, user, diff --git a/lib/new-admin/graphql/resolvers/sms.resolver.js b/lib/new-admin/graphql/resolvers/sms.resolver.js new file mode 100644 index 00000000..6936dfe8 --- /dev/null +++ b/lib/new-admin/graphql/resolvers/sms.resolver.js @@ -0,0 +1,12 @@ +const customSms = require('../../../custom-sms') + +const resolvers = { + Query: { + customMessages: () => customSms.getCustomMessages() + }, + Mutation: { + createCustomMessage: (...[, { event, deviceId, message }]) => customSms.createCustomMessage(event, deviceId, message) + } +} + +module.exports = resolvers diff --git a/lib/new-admin/graphql/types/index.js b/lib/new-admin/graphql/types/index.js index 8745bdb3..e24322ef 100644 --- a/lib/new-admin/graphql/types/index.js +++ b/lib/new-admin/graphql/types/index.js @@ -15,6 +15,7 @@ const pairing = require('./pairing.type') const rates = require('./rates.type') const scalar = require('./scalar.type') const settings = require('./settings.type') +const sms = require('./sms.type') const status = require('./status.type') const transaction = require('./transaction.type') const user = require('./users.type') @@ -36,6 +37,7 @@ const types = [ rates, scalar, settings, + sms, status, transaction, user, diff --git a/lib/new-admin/graphql/types/sms.type.js b/lib/new-admin/graphql/types/sms.type.js new file mode 100644 index 00000000..c6528b54 --- /dev/null +++ b/lib/new-admin/graphql/types/sms.type.js @@ -0,0 +1,25 @@ +const { gql } = require('apollo-server-express') + +const typeDef = gql` + type CustomMessage { + id: ID! + event: CustomMessageEvent! + deviceId: String + message: String! + } + + enum CustomMessageEvent { + smsCode + cashOutDispenseReady + } + + type Query { + customMessages: [CustomMessage] @auth + } + + type Mutation { + createCustomMessage(event: CustomMessageEvent!, deviceId: String, message: String!): CustomMessage @auth + } +` + +module.exports = typeDef diff --git a/migrations/1627518944902-custom-sms.js b/migrations/1627518944902-custom-sms.js new file mode 100644 index 00000000..ff19c1c5 --- /dev/null +++ b/migrations/1627518944902-custom-sms.js @@ -0,0 +1,20 @@ +var db = require('./db') + +exports.up = function (next) { + var sql = [ + `CREATE TYPE custom_message_event AS ENUM('sms_code', 'cash_out_dispense_ready')`, + `CREATE TABLE custom_messages ( + id UUID PRIMARY KEY, + event custom_message_event NOT NULL, + device_id TEXT REFERENCES devices(device_id), + message TEXT NOT NULL + )`, + `CREATE UNIQUE INDEX uq_custom_message_per_device ON custom_messages (event, device_id)` + ] + + db.multi(sql, next) +} + +exports.down = function (next) { + next() +} diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js new file mode 100644 index 00000000..188e7081 --- /dev/null +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js @@ -0,0 +1,127 @@ +import { useQuery } from '@apollo/react-hooks' +import { makeStyles, Box } from '@material-ui/core' +import gql from 'graphql-tag' +import * as R from 'ramda' +import React, { useState } from 'react' + +import { Link, IconButton } from 'src/components/buttons' +import DataTable from 'src/components/tables/DataTable' +import { H4 } from 'src/components/typography' +import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg' +import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg' + +import { global } from '../OperatorInfo.styles' + +import CustomSMSModal from './CustomSMSModal' + +const useStyles = makeStyles(global) + +const GET_CUSTOM_MESSAGES = gql` + query customMessages { + customMessages { + id + event + deviceId + message + } + } +` + +const GET_MACHINES = gql` + { + machines { + name + deviceId + } + } +` + +const CustomSMS = () => { + const classes = useStyles() + + const [showModal, setShowModal] = useState(false) + const { data: messagesData, loading: messagesLoading } = useQuery( + GET_CUSTOM_MESSAGES + ) + const { data: machinesData, loading: machinesLoading } = useQuery( + GET_MACHINES + ) + + const loading = messagesLoading && machinesLoading + + const machineOptions = + machinesData && + R.map( + it => ({ code: it.deviceId, display: it.name }), + R.path(['machines'])(machinesData) + ) + + const elements = [ + { + header: 'Message name', + width: 400, + size: 'sm', + textAlign: 'left', + view: it => it.event + }, + { + header: 'Edit', + width: 120, + size: 'sm', + textAlign: 'center', + view: it => ( + { + console.log('edit') + }}> + + + ) + }, + { + header: 'Delete', + width: 120, + size: 'sm', + textAlign: 'center', + view: it => ( + { + console.log('delete') + }}> + + + ) + } + ] + + return ( + <> +
+

Custom SMS message

+ + setShowModal(true)}> + Add custom SMS + + +
+ {showModal && ( + setShowModal(false)} + machineOptions={machineOptions} + /> + )} + + + ) +} + +export default CustomSMS diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js new file mode 100644 index 00000000..29bd732d --- /dev/null +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js @@ -0,0 +1,73 @@ +import { Form, Formik, Field } from 'formik' +import React from 'react' +import * as Yup from 'yup' + +import Modal from 'src/components/Modal' +import { Autocomplete } from 'src/components/inputs/formik' + +const EVENT_OPTIONS = [ + { code: 'sms_code', display: 'On SMS confirmation code' }, + { code: 'cash_out_dispense_ready', display: 'Cash out dispense ready' } +] + +const CustomSMSModal = ({ + showModal, + onClose, + customMessage, + machineOptions +}) => { + const initialValues = { + event: '', + device: '', + message: '' + } + + const validationSchema = { + event: Yup.string().required('An event is required!'), + device: Yup.string(), + message: Yup.string() + .required('The message content is required!') + .trim() + } + + return ( + <> + {showModal && ( + + +
+ + + +
+
+ )} + + ) +} + +export default CustomSMSModal diff --git a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js index 9bdfe706..d69919e0 100644 --- a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js +++ b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js @@ -10,6 +10,13 @@ const global = { position: 'relative', flex: 'wrap' }, + headerWithLink: { + display: 'flex', + position: 'relative', + alignItems: 'center', + justifyContent: 'space-between', + width: 640 + }, section: { marginBottom: 52 }, diff --git a/new-lamassu-admin/src/routing/lamassu.routes.js b/new-lamassu-admin/src/routing/lamassu.routes.js index 7394adf8..2626e30b 100644 --- a/new-lamassu-admin/src/routing/lamassu.routes.js +++ b/new-lamassu-admin/src/routing/lamassu.routes.js @@ -16,6 +16,7 @@ import MachineStatus from 'src/pages/Maintenance/MachineStatus' import Notifications from 'src/pages/Notifications/Notifications' import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar' import ContactInfo from 'src/pages/OperatorInfo/ContactInfo' +import CustomSMS from 'src/pages/OperatorInfo/CustomSMS/CustomSMS' import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting' import TermsConditions from 'src/pages/OperatorInfo/TermsConditions' import ServerLogs from 'src/pages/ServerLogs' @@ -172,6 +173,13 @@ const getLamassuRoutes = () => [ allowedRoles: [ROLES.USER, ROLES.SUPERUSER], component: ReceiptPrinting }, + { + key: 'custom-sms', + label: 'Custom SMS', + route: '/settings/operator-info/custom-sms', + allowedRoles: [ROLES.USER, ROLES.SUPERUSER], + component: CustomSMS + }, { key: 'coin-atm-radar', label: 'Coin ATM Radar', diff --git a/new-lamassu-admin/src/routing/pazuz.routes.js b/new-lamassu-admin/src/routing/pazuz.routes.js index a0d45648..eaed555c 100644 --- a/new-lamassu-admin/src/routing/pazuz.routes.js +++ b/new-lamassu-admin/src/routing/pazuz.routes.js @@ -19,6 +19,7 @@ import MachineStatus from 'src/pages/Maintenance/MachineStatus' import Notifications from 'src/pages/Notifications/Notifications' import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar' import ContactInfo from 'src/pages/OperatorInfo/ContactInfo' +import CustomSMS from 'src/pages/OperatorInfo/CustomSMS/CustomSMS' import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting' import TermsConditions from 'src/pages/OperatorInfo/TermsConditions' import ServerLogs from 'src/pages/ServerLogs' @@ -167,6 +168,13 @@ const getPazuzRoutes = () => [ allowedRoles: [ROLES.USER, ROLES.SUPERUSER], component: ReceiptPrinting }, + { + key: 'custom-sms', + label: 'Custom SMS', + route: '/settings/operator-info/custom-sms', + allowedRoles: [ROLES.USER, ROLES.SUPERUSER], + component: CustomSMS + }, { key: 'coin-atm-radar', label: 'Coin ATM Radar', From 54b73b95b4caec3afc44f729bba08ebc493bbe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Fri, 30 Jul 2021 05:28:03 +0100 Subject: [PATCH 2/6] feat: scripting-like tags on custom sms content feat: add created column to custom_messages feat: custom message dynamic validators and testing feat: delete custom sms feat: employ custom sms to existing events --- lib/custom-sms.js | 43 +++- .../graphql/resolvers/sms.resolver.js | 4 +- lib/new-admin/graphql/types/sms.type.js | 2 + lib/plugins.js | 42 ++-- lib/sms.js | 70 ++++++- migrations/1627518944902-custom-sms.js | 6 +- .../pages/OperatorInfo/CustomSMS/CustomSMS.js | 147 ++++++++++++-- .../CustomSMS/CustomSMS.styles.js | 29 +++ .../OperatorInfo/CustomSMS/CustomSMSModal.js | 189 ++++++++++++++---- .../pages/OperatorInfo/OperatorInfo.styles.js | 7 - 10 files changed, 444 insertions(+), 95 deletions(-) create mode 100644 new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.styles.js diff --git a/lib/custom-sms.js b/lib/custom-sms.js index 3ba6d9c0..f02b0dc8 100644 --- a/lib/custom-sms.js +++ b/lib/custom-sms.js @@ -1,17 +1,50 @@ +const _ = require('lodash/fp') const uuid = require('uuid') const db = require('./db') const getCustomMessages = () => { - const sql = `SELECT * FROM custom_messages` - return db.any(sql) + const sql = `SELECT * FROM custom_messages ORDER BY created` + return db.any(sql).then(res => _.map( + it => ({ + id: it.id, + event: _.camelCase(it.event), + deviceId: it.device_id, + message: it.message + }), res)) } const createCustomMessage = (event, deviceId, message) => { - const sql = `INSERT INTO custom_message (event, device_id, message) VALUES ($2, $3, $4)` - return db.none(sql, [uuid.v4(), event, deviceId, message]) + const machineId = deviceId === 'ALL_MACHINES' ? null : deviceId + const sql = `INSERT INTO custom_messages (id, event, device_id, message) VALUES ($1, $2, $3, $4)` + return db.none(sql, [uuid.v4(), _.snakeCase(event), machineId, message]) +} + +const editCustomMessage = (id, event, deviceId, message) => { + const machineId = deviceId === 'ALL_MACHINES' ? null : deviceId + const sql = `UPDATE custom_messages SET event=$2, device_id=$3, message=$4 WHERE id=$1` + return db.none(sql, [id, _.snakeCase(event), machineId, message]) +} + +const deleteCustomMessage = id => { + const sql = `DELETE FROM custom_messages WHERE id=$1` + return db.none(sql, [id]) +} + +const getCommonCustomMessages = event => { + const sql = `SELECT * FROM custom_messages WHERE event=$1 AND device_id IS NULL LIMIT 1` + return db.oneOrNone(sql, [event]) +} + +const getMachineCustomMessages = (event, deviceId) => { + const sql = `SELECT * FROM custom_messages WHERE event=$1 AND device_id=$2 LIMIT 1` + return db.oneOrNone(sql, [event, deviceId]) } module.exports = { getCustomMessages, - createCustomMessage + createCustomMessage, + editCustomMessage, + deleteCustomMessage, + getCommonCustomMessages, + getMachineCustomMessages } diff --git a/lib/new-admin/graphql/resolvers/sms.resolver.js b/lib/new-admin/graphql/resolvers/sms.resolver.js index 6936dfe8..a9c2ba5e 100644 --- a/lib/new-admin/graphql/resolvers/sms.resolver.js +++ b/lib/new-admin/graphql/resolvers/sms.resolver.js @@ -5,7 +5,9 @@ const resolvers = { customMessages: () => customSms.getCustomMessages() }, Mutation: { - createCustomMessage: (...[, { event, deviceId, message }]) => customSms.createCustomMessage(event, deviceId, message) + createCustomMessage: (...[, { event, deviceId, message }]) => customSms.createCustomMessage(event, deviceId, message), + editCustomMessage: (...[, { id, event, deviceId, message }]) => customSms.editCustomMessage(id, event, deviceId, message), + deleteCustomMessage: (...[, { id }]) => customSms.deleteCustomMessage(id) } } diff --git a/lib/new-admin/graphql/types/sms.type.js b/lib/new-admin/graphql/types/sms.type.js index c6528b54..2eb1c62e 100644 --- a/lib/new-admin/graphql/types/sms.type.js +++ b/lib/new-admin/graphql/types/sms.type.js @@ -19,6 +19,8 @@ const typeDef = gql` type Mutation { createCustomMessage(event: CustomMessageEvent!, deviceId: String, message: String!): CustomMessage @auth + editCustomMessage(id: ID!, event: CustomMessageEvent!, deviceId: String, message: String!): CustomMessage @auth + deleteCustomMessage(id: ID!): CustomMessage @auth } ` diff --git a/lib/plugins.js b/lib/plugins.js index ce133e8c..1c62cf7e 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -361,19 +361,19 @@ function plugins (settings, deviceId) { const phone = tx.phone const timestamp = dateFormat(new Date(), 'UTC:HH:MM Z') - const rec = { - sms: { - toNumber: phone, - body: `Your cash is waiting! Go to the Cryptomat and press Redeem within 24 hours. [${timestamp}]` - } - } + return sms.getCashOutReadySms(deviceId, phone, code) + .then(msg => { + const rec = { + sms: msg + } + + return sms.sendMessage(settings, rec) + .then(() => { + const sql = 'update cash_out_txs set notified=$1 where id=$2' + const values = [true, tx.id] - return sms.sendMessage(settings, rec) - .then(() => { - const sql = 'update cash_out_txs set notified=$1 where id=$2' - const values = [true, tx.id] - - return db.none(sql, values) + return db.none(sql, values) + }) }) } @@ -723,15 +723,15 @@ function plugins (settings, deviceId) { ? '123' : randomCode() - const rec = { - sms: { - toNumber: phone, - body: 'Your cryptomat code: ' + code - } - } - - return sms.sendMessage(settings, rec) - .then(() => code) + return sms.getPhoneCodeSms(deviceId, phone, code) + .then(msg => { + const rec = { + sms: msg + } + + return sms.sendMessage(settings, rec) + .then(() => code) + }) } function sweepHdRow (row) { diff --git a/lib/sms.js b/lib/sms.js index 597f5bd1..c1cbd08c 100644 --- a/lib/sms.js +++ b/lib/sms.js @@ -1,6 +1,67 @@ const ph = require('./plugin-helper') const argv = require('minimist')(process.argv.slice(2)) const { utils: coinUtils } = require('lamassu-coins') +const _ = require('lodash/fp') + +const customSms = require('./custom-sms') + +function getPhoneCodeSms (deviceId, phone, code) { + return Promise.all([ + customSms.getCommonCustomMessages('sms_code'), + customSms.getMachineCustomMessages('sms_code', deviceId) + ]) + .then(([commonMsg, machineMsg]) => { + if (!_.isNil(machineMsg)) { + const messageContent = _.replace('#code', code, machineMsg.message) + return { + toNumber: phone, + body: messageContent + } + } + + if (!_.isNil(commonMsg)) { + const messageContent = _.replace('#code', code, commonMsg.message) + return { + toNumber: phone, + body: messageContent + } + } + + return { + toNumber: phone, + body: `Your cryptomat code: ${code}` + } + }) +} + +function getCashOutReadySms (deviceId, phone, timestamp) { + return Promise.all([ + customSms.getCommonCustomMessages('cash_out_dispense_ready'), + customSms.getMachineCustomMessages('cash_out_dispense_ready', deviceId) + ]) + .then(([commonMsg, machineMsg]) => { + if (!_.isNil(machineMsg)) { + const messageContent = _.replace('#timestamp', timestamp, machineMsg.message) + return { + toNumber: phone, + body: messageContent + } + } + + if (!_.isNil(commonMsg)) { + const messageContent = _.replace('#timestamp', timestamp, commonMsg.message) + return { + toNumber: phone, + body: messageContent + } + } + + return { + toNumber: phone, + body: `Your cash is waiting! Go to the Cryptomat and press Redeem within 24 hours. [${timestamp}]` + } + }) +} function getPlugin (settings) { const pluginCode = argv.mockSms ? 'mock-sms' : 'twilio' @@ -91,4 +152,11 @@ function formatSmsReceipt (data, options) { return request } -module.exports = { sendMessage, formatSmsReceipt, getLookup, toCryptoUnits } +module.exports = { + getPhoneCodeSms, + getCashOutReadySms, + sendMessage, + getLookup, + formatSmsReceipt, + toCryptoUnits +} diff --git a/migrations/1627518944902-custom-sms.js b/migrations/1627518944902-custom-sms.js index ff19c1c5..95395d31 100644 --- a/migrations/1627518944902-custom-sms.js +++ b/migrations/1627518944902-custom-sms.js @@ -7,9 +7,11 @@ exports.up = function (next) { id UUID PRIMARY KEY, event custom_message_event NOT NULL, device_id TEXT REFERENCES devices(device_id), - message TEXT NOT NULL + message TEXT NOT NULL, + created TIMESTAMPTZ NOT NULL DEFAULT now() )`, - `CREATE UNIQUE INDEX uq_custom_message_per_device ON custom_messages (event, device_id)` + `CREATE UNIQUE INDEX uq_custom_message_per_device ON custom_messages (event, device_id) WHERE device_id IS NOT NULL`, + `CREATE UNIQUE INDEX uq_custom_message_all_devices ON custom_messages (event) WHERE device_id IS NULL` ] db.multi(sql, next) diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js index 188e7081..44bcf845 100644 --- a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js @@ -1,20 +1,20 @@ -import { useQuery } from '@apollo/react-hooks' +import { useQuery, useMutation } from '@apollo/react-hooks' import { makeStyles, Box } from '@material-ui/core' import gql from 'graphql-tag' import * as R from 'ramda' import React, { useState } from 'react' +import { DeleteDialog } from 'src/components/DeleteDialog' import { Link, IconButton } from 'src/components/buttons' import DataTable from 'src/components/tables/DataTable' import { H4 } from 'src/components/typography' import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg' import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg' -import { global } from '../OperatorInfo.styles' - +import styles from './CustomSMS.styles' import CustomSMSModal from './CustomSMSModal' -const useStyles = makeStyles(global) +const useStyles = makeStyles(styles) const GET_CUSTOM_MESSAGES = gql` query customMessages { @@ -27,6 +27,44 @@ const GET_CUSTOM_MESSAGES = gql` } ` +const CREATE_CUSTOM_MESSAGE = gql` + mutation createCustomMessage( + $event: CustomMessageEvent! + $deviceId: String! + $message: String! + ) { + createCustomMessage(event: $event, deviceId: $deviceId, message: $message) { + id + } + } +` + +const EDIT_CUSTOM_MESSAGE = gql` + mutation editCustomMessage( + $id: ID! + $event: CustomMessageEvent! + $deviceId: String! + $message: String! + ) { + editCustomMessage( + id: $id + event: $event + deviceId: $deviceId + message: $message + ) { + id + } + } +` + +const DELETE_CUSTOM_MESSAGE = gql` + mutation deleteCustomMessage($id: ID!) { + deleteCustomMessage(id: $id) { + id + } + } +` + const GET_MACHINES = gql` { machines { @@ -36,43 +74,91 @@ const GET_MACHINES = gql` } ` +const EVENT_OPTIONS = [ + { code: 'smsCode', display: 'On SMS confirmation code' }, + { code: 'cashOutDispenseReady', display: 'Cash out dispense ready' } +] + const CustomSMS = () => { const classes = useStyles() + const [deleteDialog, setDeleteDialog] = useState(false) const [showModal, setShowModal] = useState(false) + const [selectedSMS, setSelectedSMS] = useState(null) + const [errorMsg, setErrorMsg] = useState('') + const { data: messagesData, loading: messagesLoading } = useQuery( GET_CUSTOM_MESSAGES ) + const { data: machinesData, loading: machinesLoading } = useQuery( GET_MACHINES ) + const [createMessage] = useMutation(CREATE_CUSTOM_MESSAGE, { + onError: ({ msg }) => setErrorMsg(msg), + refetchQueries: () => ['customMessages'] + }) + + const [editMessage] = useMutation(EDIT_CUSTOM_MESSAGE, { + onError: ({ msg }) => setErrorMsg(msg), + refetchQueries: () => ['customMessages'] + }) + + const [deleteMessage] = useMutation(DELETE_CUSTOM_MESSAGE, { + onError: ({ msg }) => setErrorMsg(msg), + refetchQueries: () => ['customMessages'] + }) + const loading = messagesLoading && machinesLoading const machineOptions = - machinesData && - R.map( - it => ({ code: it.deviceId, display: it.name }), - R.path(['machines'])(machinesData) - ) + (machinesData && + R.map( + it => ({ code: it.deviceId, display: it.name }), + R.path(['machines'])(machinesData) + )) ?? + [] + + const handleClose = () => { + setSelectedSMS(null) + setShowModal(false) + setDeleteDialog(false) + } + + const handleOpen = () => { + setErrorMsg('') + setShowModal(true) + } const elements = [ { - header: 'Message name', + header: 'Event', width: 400, size: 'sm', textAlign: 'left', - view: it => it.event + view: it => + R.find(ite => R.propEq('event', ite.code, it), EVENT_OPTIONS).display + }, + { + header: 'Machine', + width: 200, + size: 'sm', + textAlign: 'left', + view: it => + R.find(ite => R.propEq('deviceId', ite.code, it), machineOptions) + ?.display ?? `All Machines` }, { header: 'Edit', - width: 120, + width: 100, size: 'sm', textAlign: 'center', view: it => ( { - console.log('edit') + setSelectedSMS(it) + setShowModal(true) }}> @@ -80,13 +166,14 @@ const CustomSMS = () => { }, { header: 'Delete', - width: 120, + width: 100, size: 'sm', textAlign: 'center', view: it => ( { - console.log('delete') + setSelectedSMS(it) + setDeleteDialog(true) }}> @@ -96,13 +183,10 @@ const CustomSMS = () => { return ( <> -
+

Custom SMS message

- - setShowModal(true)}> + + handleOpen()}> Add custom SMS @@ -110,10 +194,29 @@ const CustomSMS = () => { {showModal && ( setShowModal(false)} + onClose={handleClose} machineOptions={machineOptions} + eventOptions={EVENT_OPTIONS} + sms={selectedSMS} + creationError={errorMsg} + submit={selectedSMS ? editMessage : createMessage} /> )} + { + handleClose() + }} + onConfirmed={() => { + handleClose() + deleteMessage({ + variables: { + id: selectedSMS.id + } + }) + }} + errorMessage={errorMsg} + /> *': { + marginTop: 20 + }, + display: 'flex', + flexDirection: 'column', + height: '100%' + }, + footer: { + display: 'flex', + flexDirection: 'row', + margin: [['auto', 0, spacer * 3, 0]] + }, + submit: { + margin: [['auto', 0, 0, 'auto']] + } +} + +export default styles diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js index 29bd732d..ca8a4d85 100644 --- a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js @@ -1,40 +1,128 @@ +import { makeStyles } from '@material-ui/core' import { Form, Formik, Field } from 'formik' -import React from 'react' +import * as R from 'ramda' +import React, { useState } from 'react' import * as Yup from 'yup' +import ErrorMessage from 'src/components/ErrorMessage' import Modal from 'src/components/Modal' -import { Autocomplete } from 'src/components/inputs/formik' +import { Button } from 'src/components/buttons' +import { Autocomplete, TextInput } from 'src/components/inputs/formik' -const EVENT_OPTIONS = [ - { code: 'sms_code', display: 'On SMS confirmation code' }, - { code: 'cash_out_dispense_ready', display: 'Cash out dispense ready' } -] +import styles from './CustomSMS.styles' + +const useStyles = makeStyles(styles) + +const ALL_MACHINES = { + code: 'ALL_MACHINES', + display: 'All Machines' +} + +const getErrorMsg = (formikErrors, formikTouched, mutationError) => { + if (!formikErrors || !formikTouched) return null + if (mutationError) return 'Internal server error' + if (formikErrors.event && formikTouched.event) return formikErrors.event + if (formikErrors.message && formikTouched.message) return formikErrors.message + return null +} + +const prefill = { + smsCode: { + tags: [], + validator: Yup.string() + .required('The message content is required!') + .trim() + .test({ + name: 'has-code-tag', + message: 'A #code tag is missing from the message!', + exclusive: false, + test: value => value?.match(/#code/g || [])?.length > 0 + }) + .test({ + name: 'has-single-code-tag', + message: 'There should be a single #code tag!', + exclusive: false, + test: value => value?.match(/#code/g || [])?.length === 1 + }) + }, + cashOutDispenseReady: { + tags: [], + validator: Yup.string() + .required('The message content is required!') + .trim() + .test({ + name: 'has-timestamp-tag', + message: 'A #timestamp tag is missing from the message!', + exclusive: false, + test: value => value?.match(/#timestamp/g || [])?.length > 0 + }) + .test({ + name: 'has-single-timestamp-tag', + message: 'There should be a single #timestamp tag!', + exclusive: false, + test: value => value?.match(/#timestamp/g || [])?.length === 1 + }) + } +} const CustomSMSModal = ({ showModal, onClose, - customMessage, - machineOptions + sms, + machineOptions, + eventOptions, + creationError, + submit }) => { + const classes = useStyles() + + const [selectedEvent, setSelectedEvent] = useState(sms?.event) + const initialValues = { - event: '', - device: '', - message: '' + event: !R.isNil(sms) ? sms.event : '', + device: !R.isNil(sms) + ? !R.isNil(sms.deviceId) + ? sms.deviceId + : 'ALL_MACHINES' + : '', + message: !R.isNil(sms) ? sms.message : '' } - const validationSchema = { + const validationSchema = Yup.object().shape({ event: Yup.string().required('An event is required!'), - device: Yup.string(), - message: Yup.string() - .required('The message content is required!') - .trim() + device: Yup.string().required('A machine is required!'), + message: + prefill[selectedEvent]?.validator ?? + Yup.string() + .required('The message content is required!') + .trim() + }) + + const handleSubmit = values => { + sms + ? submit({ + variables: { + id: sms.id, + event: values.event, + deviceId: values.device, + message: values.message + } + }) + : submit({ + variables: { + event: values.event, + deviceId: values.device, + message: values.message + } + }) + onClose() } return ( <> {showModal && ( -
- - - + validationSchema={validationSchema} + onSubmit={(values, errors, touched) => + handleSubmit(values, errors, touched) + }> + {({ values, errors, touched }) => ( +
+ + + +
+ {getErrorMsg(errors, touched, creationError) && ( + + {getErrorMsg(errors, touched, creationError)} + + )} + +
+ + )}
)} diff --git a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js index d69919e0..9bdfe706 100644 --- a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js +++ b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js @@ -10,13 +10,6 @@ const global = { position: 'relative', flex: 'wrap' }, - headerWithLink: { - display: 'flex', - position: 'relative', - alignItems: 'center', - justifyContent: 'space-between', - width: 640 - }, section: { marginBottom: 52 }, From 30bc9c96dadc6c685a90dfc1c9e659d3b2bb3113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Fri, 30 Jul 2021 05:37:06 +0100 Subject: [PATCH 3/6] fix: missing timestamp variable fix: remove tags array --- lib/plugins.js | 2 +- .../src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index 1c62cf7e..a3898d4d 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -361,7 +361,7 @@ function plugins (settings, deviceId) { const phone = tx.phone const timestamp = dateFormat(new Date(), 'UTC:HH:MM Z') - return sms.getCashOutReadySms(deviceId, phone, code) + return sms.getCashOutReadySms(deviceId, phone, timestamp) .then(msg => { const rec = { sms: msg diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js index ca8a4d85..37201990 100644 --- a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMSModal.js @@ -28,7 +28,6 @@ const getErrorMsg = (formikErrors, formikTouched, mutationError) => { const prefill = { smsCode: { - tags: [], validator: Yup.string() .required('The message content is required!') .trim() @@ -46,7 +45,6 @@ const prefill = { }) }, cashOutDispenseReady: { - tags: [], validator: Yup.string() .required('The message content is required!') .trim() From ee8c46b51ea56d40a2a9e26caaa5dd05e7881e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 25 Aug 2021 16:56:07 +0100 Subject: [PATCH 4/6] fix: remove machine selector --- lib/custom-sms.js | 27 ++++------ .../graphql/resolvers/sms.resolver.js | 4 +- lib/new-admin/graphql/types/sms.type.js | 5 +- lib/plugins.js | 4 +- lib/sms.js | 42 ++++----------- migrations/1627518944902-custom-sms.js | 7 +-- .../pages/OperatorInfo/CustomSMS/CustomSMS.js | 52 ++----------------- .../OperatorInfo/CustomSMS/CustomSMSModal.js | 23 -------- 8 files changed, 32 insertions(+), 132 deletions(-) diff --git a/lib/custom-sms.js b/lib/custom-sms.js index f02b0dc8..8cb8546a 100644 --- a/lib/custom-sms.js +++ b/lib/custom-sms.js @@ -8,21 +8,18 @@ const getCustomMessages = () => { it => ({ id: it.id, event: _.camelCase(it.event), - deviceId: it.device_id, message: it.message }), res)) } -const createCustomMessage = (event, deviceId, message) => { - const machineId = deviceId === 'ALL_MACHINES' ? null : deviceId - const sql = `INSERT INTO custom_messages (id, event, device_id, message) VALUES ($1, $2, $3, $4)` - return db.none(sql, [uuid.v4(), _.snakeCase(event), machineId, message]) +const createCustomMessage = (event, message) => { + const sql = `INSERT INTO custom_messages (id, event, message) VALUES ($1, $2, $3)` + return db.none(sql, [uuid.v4(), _.snakeCase(event), message]) } -const editCustomMessage = (id, event, deviceId, message) => { - const machineId = deviceId === 'ALL_MACHINES' ? null : deviceId - const sql = `UPDATE custom_messages SET event=$2, device_id=$3, message=$4 WHERE id=$1` - return db.none(sql, [id, _.snakeCase(event), machineId, message]) +const editCustomMessage = (id, event, message) => { + const sql = `UPDATE custom_messages SET event=$2, message=$3 WHERE id=$1` + return db.none(sql, [id, _.snakeCase(event), message]) } const deleteCustomMessage = id => { @@ -30,21 +27,15 @@ const deleteCustomMessage = id => { return db.none(sql, [id]) } -const getCommonCustomMessages = event => { - const sql = `SELECT * FROM custom_messages WHERE event=$1 AND device_id IS NULL LIMIT 1` +const getCustomMessage = event => { + const sql = `SELECT * FROM custom_messages WHERE event=$1 LIMIT 1` return db.oneOrNone(sql, [event]) } -const getMachineCustomMessages = (event, deviceId) => { - const sql = `SELECT * FROM custom_messages WHERE event=$1 AND device_id=$2 LIMIT 1` - return db.oneOrNone(sql, [event, deviceId]) -} - module.exports = { getCustomMessages, createCustomMessage, editCustomMessage, deleteCustomMessage, - getCommonCustomMessages, - getMachineCustomMessages + getCustomMessage } diff --git a/lib/new-admin/graphql/resolvers/sms.resolver.js b/lib/new-admin/graphql/resolvers/sms.resolver.js index a9c2ba5e..f159c8c4 100644 --- a/lib/new-admin/graphql/resolvers/sms.resolver.js +++ b/lib/new-admin/graphql/resolvers/sms.resolver.js @@ -5,8 +5,8 @@ const resolvers = { customMessages: () => customSms.getCustomMessages() }, Mutation: { - createCustomMessage: (...[, { event, deviceId, message }]) => customSms.createCustomMessage(event, deviceId, message), - editCustomMessage: (...[, { id, event, deviceId, message }]) => customSms.editCustomMessage(id, event, deviceId, message), + createCustomMessage: (...[, { event, message }]) => customSms.createCustomMessage(event, message), + editCustomMessage: (...[, { id, event, message }]) => customSms.editCustomMessage(id, event, message), deleteCustomMessage: (...[, { id }]) => customSms.deleteCustomMessage(id) } } diff --git a/lib/new-admin/graphql/types/sms.type.js b/lib/new-admin/graphql/types/sms.type.js index 2eb1c62e..a86947b7 100644 --- a/lib/new-admin/graphql/types/sms.type.js +++ b/lib/new-admin/graphql/types/sms.type.js @@ -4,7 +4,6 @@ const typeDef = gql` type CustomMessage { id: ID! event: CustomMessageEvent! - deviceId: String message: String! } @@ -18,8 +17,8 @@ const typeDef = gql` } type Mutation { - createCustomMessage(event: CustomMessageEvent!, deviceId: String, message: String!): CustomMessage @auth - editCustomMessage(id: ID!, event: CustomMessageEvent!, deviceId: String, message: String!): CustomMessage @auth + createCustomMessage(event: CustomMessageEvent!, message: String!): CustomMessage @auth + editCustomMessage(id: ID!, event: CustomMessageEvent!, message: String!): CustomMessage @auth deleteCustomMessage(id: ID!): CustomMessage @auth } ` diff --git a/lib/plugins.js b/lib/plugins.js index a3898d4d..8c59b218 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -361,7 +361,7 @@ function plugins (settings, deviceId) { const phone = tx.phone const timestamp = dateFormat(new Date(), 'UTC:HH:MM Z') - return sms.getCashOutReadySms(deviceId, phone, timestamp) + return sms.getCashOutReadySms(phone, timestamp) .then(msg => { const rec = { sms: msg @@ -723,7 +723,7 @@ function plugins (settings, deviceId) { ? '123' : randomCode() - return sms.getPhoneCodeSms(deviceId, phone, code) + return sms.getPhoneCodeSms(phone, code) .then(msg => { const rec = { sms: msg diff --git a/lib/sms.js b/lib/sms.js index c1cbd08c..9d6e66cd 100644 --- a/lib/sms.js +++ b/lib/sms.js @@ -5,22 +5,11 @@ const _ = require('lodash/fp') const customSms = require('./custom-sms') -function getPhoneCodeSms (deviceId, phone, code) { - return Promise.all([ - customSms.getCommonCustomMessages('sms_code'), - customSms.getMachineCustomMessages('sms_code', deviceId) - ]) - .then(([commonMsg, machineMsg]) => { - if (!_.isNil(machineMsg)) { - const messageContent = _.replace('#code', code, machineMsg.message) - return { - toNumber: phone, - body: messageContent - } - } - - if (!_.isNil(commonMsg)) { - const messageContent = _.replace('#code', code, commonMsg.message) +function getPhoneCodeSms (phone, code) { + return customSms.getCustomMessage('sms_code') + .then(msg => { + if (!_.isNil(msg)) { + const messageContent = _.replace('#code', code, msg.message) return { toNumber: phone, body: messageContent @@ -34,22 +23,11 @@ function getPhoneCodeSms (deviceId, phone, code) { }) } -function getCashOutReadySms (deviceId, phone, timestamp) { - return Promise.all([ - customSms.getCommonCustomMessages('cash_out_dispense_ready'), - customSms.getMachineCustomMessages('cash_out_dispense_ready', deviceId) - ]) - .then(([commonMsg, machineMsg]) => { - if (!_.isNil(machineMsg)) { - const messageContent = _.replace('#timestamp', timestamp, machineMsg.message) - return { - toNumber: phone, - body: messageContent - } - } - - if (!_.isNil(commonMsg)) { - const messageContent = _.replace('#timestamp', timestamp, commonMsg.message) +function getCashOutReadySms (phone, timestamp) { + return customSms.getCustomMessage('cash_out_dispense_ready') + .then(msg => { + if (!_.isNil(msg)) { + const messageContent = _.replace('#timestamp', timestamp, msg.message) return { toNumber: phone, body: messageContent diff --git a/migrations/1627518944902-custom-sms.js b/migrations/1627518944902-custom-sms.js index 95395d31..74b82999 100644 --- a/migrations/1627518944902-custom-sms.js +++ b/migrations/1627518944902-custom-sms.js @@ -5,13 +5,10 @@ exports.up = function (next) { `CREATE TYPE custom_message_event AS ENUM('sms_code', 'cash_out_dispense_ready')`, `CREATE TABLE custom_messages ( id UUID PRIMARY KEY, - event custom_message_event NOT NULL, - device_id TEXT REFERENCES devices(device_id), + event custom_message_event UNIQUE NOT NULL, message TEXT NOT NULL, created TIMESTAMPTZ NOT NULL DEFAULT now() - )`, - `CREATE UNIQUE INDEX uq_custom_message_per_device ON custom_messages (event, device_id) WHERE device_id IS NOT NULL`, - `CREATE UNIQUE INDEX uq_custom_message_all_devices ON custom_messages (event) WHERE device_id IS NULL` + )` ] db.multi(sql, next) diff --git a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js index 44bcf845..b1d3dac0 100644 --- a/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js +++ b/new-lamassu-admin/src/pages/OperatorInfo/CustomSMS/CustomSMS.js @@ -21,19 +21,14 @@ const GET_CUSTOM_MESSAGES = gql` customMessages { id event - deviceId message } } ` const CREATE_CUSTOM_MESSAGE = gql` - mutation createCustomMessage( - $event: CustomMessageEvent! - $deviceId: String! - $message: String! - ) { - createCustomMessage(event: $event, deviceId: $deviceId, message: $message) { + mutation createCustomMessage($event: CustomMessageEvent!, $message: String!) { + createCustomMessage(event: $event, message: $message) { id } } @@ -43,15 +38,9 @@ const EDIT_CUSTOM_MESSAGE = gql` mutation editCustomMessage( $id: ID! $event: CustomMessageEvent! - $deviceId: String! $message: String! ) { - editCustomMessage( - id: $id - event: $event - deviceId: $deviceId - message: $message - ) { + editCustomMessage(id: $id, event: $event, message: $message) { id } } @@ -65,15 +54,6 @@ const DELETE_CUSTOM_MESSAGE = gql` } ` -const GET_MACHINES = gql` - { - machines { - name - deviceId - } - } -` - const EVENT_OPTIONS = [ { code: 'smsCode', display: 'On SMS confirmation code' }, { code: 'cashOutDispenseReady', display: 'Cash out dispense ready' } @@ -91,10 +71,6 @@ const CustomSMS = () => { GET_CUSTOM_MESSAGES ) - const { data: machinesData, loading: machinesLoading } = useQuery( - GET_MACHINES - ) - const [createMessage] = useMutation(CREATE_CUSTOM_MESSAGE, { onError: ({ msg }) => setErrorMsg(msg), refetchQueries: () => ['customMessages'] @@ -110,15 +86,7 @@ const CustomSMS = () => { refetchQueries: () => ['customMessages'] }) - const loading = messagesLoading && machinesLoading - - const machineOptions = - (machinesData && - R.map( - it => ({ code: it.deviceId, display: it.name }), - R.path(['machines'])(machinesData) - )) ?? - [] + const loading = messagesLoading const handleClose = () => { setSelectedSMS(null) @@ -134,21 +102,12 @@ const CustomSMS = () => { const elements = [ { header: 'Event', - width: 400, + width: 600, size: 'sm', textAlign: 'left', view: it => R.find(ite => R.propEq('event', ite.code, it), EVENT_OPTIONS).display }, - { - header: 'Machine', - width: 200, - size: 'sm', - textAlign: 'left', - view: it => - R.find(ite => R.propEq('deviceId', ite.code, it), machineOptions) - ?.display ?? `All Machines` - }, { header: 'Edit', width: 100, @@ -195,7 +154,6 @@ const CustomSMS = () => { { if (!formikErrors || !formikTouched) return null if (mutationError) return 'Internal server error' @@ -67,7 +62,6 @@ const CustomSMSModal = ({ showModal, onClose, sms, - machineOptions, eventOptions, creationError, submit @@ -78,17 +72,11 @@ const CustomSMSModal = ({ const initialValues = { event: !R.isNil(sms) ? sms.event : '', - device: !R.isNil(sms) - ? !R.isNil(sms.deviceId) - ? sms.deviceId - : 'ALL_MACHINES' - : '', message: !R.isNil(sms) ? sms.message : '' } const validationSchema = Yup.object().shape({ event: Yup.string().required('An event is required!'), - device: Yup.string().required('A machine is required!'), message: prefill[selectedEvent]?.validator ?? Yup.string() @@ -102,14 +90,12 @@ const CustomSMSModal = ({ variables: { id: sms.id, event: values.event, - deviceId: values.device, message: values.message } }) : submit({ variables: { event: values.event, - deviceId: values.device, message: values.message } }) @@ -146,15 +132,6 @@ const CustomSMSModal = ({ valueProp="code" component={Autocomplete} /> - Date: Mon, 30 Aug 2021 16:02:23 +0100 Subject: [PATCH 5/6] refactor: unify sms creation into a single function --- lib/plugins.js | 14 +++++++------- lib/sms.js | 36 ++++++++++++------------------------ 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index 8c59b218..5b38eb7a 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -361,15 +361,15 @@ function plugins (settings, deviceId) { const phone = tx.phone const timestamp = dateFormat(new Date(), 'UTC:HH:MM Z') - return sms.getCashOutReadySms(phone, timestamp) - .then(msg => { + return sms.getSms('cash_out_dispense_ready', phone, { timestamp }) + .then(smsObj => { const rec = { - sms: msg + sms: smsObj } return sms.sendMessage(settings, rec) .then(() => { - const sql = 'update cash_out_txs set notified=$1 where id=$2' + const sql = 'UPDATE cash_out_txs SET notified=$1 WHERE id=$2' const values = [true, tx.id] return db.none(sql, values) @@ -723,10 +723,10 @@ function plugins (settings, deviceId) { ? '123' : randomCode() - return sms.getPhoneCodeSms(phone, code) - .then(msg => { + return sms.getSms('sms_code', phone, { code }) + .then(smsObj => { const rec = { - sms: msg + sms: smsObj } return sms.sendMessage(settings, rec) diff --git a/lib/sms.js b/lib/sms.js index 9d6e66cd..71b691c2 100644 --- a/lib/sms.js +++ b/lib/sms.js @@ -5,11 +5,18 @@ const _ = require('lodash/fp') const customSms = require('./custom-sms') -function getPhoneCodeSms (phone, code) { - return customSms.getCustomMessage('sms_code') +const getDefaultMessageContent = content => ({ + smsCode: `Your cryptomat code: ${content.code}`, + cashOutDispenseReady: `Your cash is waiting! Go to the Cryptomat and press Redeem within 24 hours. [${content.timestamp}]` +}) + +function getSms (event, phone, content) { + return customSms.getCustomMessage(event) .then(msg => { if (!_.isNil(msg)) { - const messageContent = _.replace('#code', code, msg.message) + var accMsg = msg.message + const contentKeys = _.keys(content) + const messageContent = _.reduce((acc, it) => _.replace(`#${it}`, content[it], acc), accMsg, contentKeys) return { toNumber: phone, body: messageContent @@ -18,25 +25,7 @@ function getPhoneCodeSms (phone, code) { return { toNumber: phone, - body: `Your cryptomat code: ${code}` - } - }) -} - -function getCashOutReadySms (phone, timestamp) { - return customSms.getCustomMessage('cash_out_dispense_ready') - .then(msg => { - if (!_.isNil(msg)) { - const messageContent = _.replace('#timestamp', timestamp, msg.message) - return { - toNumber: phone, - body: messageContent - } - } - - return { - toNumber: phone, - body: `Your cash is waiting! Go to the Cryptomat and press Redeem within 24 hours. [${timestamp}]` + body: getDefaultMessageContent(content)[_.camelCase(event)] } }) } @@ -131,8 +120,7 @@ function formatSmsReceipt (data, options) { } module.exports = { - getPhoneCodeSms, - getCashOutReadySms, + getSms, sendMessage, getLookup, formatSmsReceipt, From d759704db3c18174c1148aadca3d48b0f43a905d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Mon, 11 Oct 2021 01:58:00 +0100 Subject: [PATCH 6/6] refactor: pull up variable to constants file --- lib/constants.js | 7 ++++++- lib/plugins.js | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/constants.js b/lib/constants.js index 9e526ce6..94bb3138 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -16,6 +16,9 @@ const USER_SESSIONS_CLEAR_INTERVAL = 1 * T.hour const AUTOMATIC = 'automatic' const MANUAL = 'manual' +const CASH_OUT_DISPENSE_READY = 'cash_out_dispense_ready' +const CONFIRMATION_CODE = 'sms_code' + module.exports = { anonymousCustomer, cassetteMaxCapacity, @@ -25,5 +28,7 @@ module.exports = { AUTOMATIC, MANUAL, USER_SESSIONS_TABLE_NAME, - USER_SESSIONS_CLEAR_INTERVAL + USER_SESSIONS_CLEAR_INTERVAL, + CASH_OUT_DISPENSE_READY, + CONFIRMATION_CODE } diff --git a/lib/plugins.js b/lib/plugins.js index 5b38eb7a..bf53a935 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -23,7 +23,7 @@ const customers = require('./customers') const commissionMath = require('./commission-math') const loyalty = require('./loyalty') -const { cassetteMaxCapacity } = require('./constants') +const { cassetteMaxCapacity, CASH_OUT_DISPENSE_READY, CONFIRMATION_CODE } = require('./constants') const notifier = require('./notifier') @@ -361,7 +361,7 @@ function plugins (settings, deviceId) { const phone = tx.phone const timestamp = dateFormat(new Date(), 'UTC:HH:MM Z') - return sms.getSms('cash_out_dispense_ready', phone, { timestamp }) + return sms.getSms(CASH_OUT_DISPENSE_READY, phone, { timestamp }) .then(smsObj => { const rec = { sms: smsObj @@ -723,7 +723,7 @@ function plugins (settings, deviceId) { ? '123' : randomCode() - return sms.getSms('sms_code', phone, { code }) + return sms.getSms(CONFIRMATION_CODE, phone, { code }) .then(smsObj => { const rec = { sms: smsObj