feat: implement subscriber info retrieval

This commit is contained in:
Sérgio Salgado 2021-08-05 01:26:05 +01:00 committed by Josh Harvey
parent 149a2f99c8
commit a6eb4b904f
7 changed files with 110 additions and 11 deletions

View file

@ -21,6 +21,8 @@ const NUM_RESULTS = 1000
const idPhotoCardBasedir = _.get('idPhotoCardDir', options) const idPhotoCardBasedir = _.get('idPhotoCardDir', options)
const frontCameraBaseDir = _.get('frontCameraDir', options) const frontCameraBaseDir = _.get('frontCameraDir', options)
const operatorDataDir = _.get('operatorDataDir', options) const operatorDataDir = _.get('operatorDataDir', options)
const sms = require('./sms')
const settingsLoader = require('./new-settings-loader')
const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel'] const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel']
@ -117,13 +119,21 @@ async function updateCustomer (id, data, userToken) {
const enhancedUpdateData = enhanceAtFields(enhanceOverrideFields(formattedData, userToken)) const enhancedUpdateData = enhanceAtFields(enhanceOverrideFields(formattedData, userToken))
const updateData = updateOverride(enhancedUpdateData) const updateData = updateOverride(enhancedUpdateData)
const sql = Pgp.helpers.update(updateData, _.keys(updateData), 'customers') + if (!_.isEmpty(updateData)) {
const sql = Pgp.helpers.update(updateData, _.keys(updateData), 'customers') +
' where id=$1' ' where id=$1'
await db.none(sql, [id])
}
if (data.subscriberInfo) {
Promise.all([getCustomerById(id), settingsLoader.loadLatest()])
.then(([customer, config]) => sms.getLookup(config, customer.phone))
.then(res => updateSubscriberData(id, res, userToken))
.catch(console.error)
}
invalidateCustomerNotifications(id, formattedData) invalidateCustomerNotifications(id, formattedData)
await db.none(sql, [id])
return getCustomerById(id) return getCustomerById(id)
} }
@ -134,6 +144,11 @@ const invalidateCustomerNotifications = (id, data) => {
return notifierQueries.invalidateNotification(detailB, 'compliance') return notifierQueries.invalidateNotification(detailB, 'compliance')
} }
const updateSubscriberData = (customerId, data, userToken) => {
const sql = `UPDATE customers SET subscriber_info=$1, subscriber_info_at=now(), subscriber_info_by=$2 WHERE id=$3`
return db.none(sql, [data, userToken, customerId])
}
/** /**
* Get customer by id * Get customer by id
* *
@ -508,7 +523,7 @@ function getCustomerById (id) {
phone, sms_override, id_card_data, id_card_data_override, id_card_data_expiration, phone, sms_override, id_card_data, id_card_data_override, id_card_data_expiration,
id_card_photo_path, id_card_photo_override, us_ssn, us_ssn_override, sanctions, sanctions_at, id_card_photo_path, id_card_photo_override, us_ssn, us_ssn_override, sanctions, sanctions_at,
sanctions_override, total_txs, total_spent, created as last_active, fiat as last_tx_fiat, sanctions_override, total_txs, total_spent, created as last_active, fiat as last_tx_fiat,
fiat_code as last_tx_fiat_code, tx_class as last_tx_class fiat_code as last_tx_fiat_code, tx_class as last_tx_class, subscriber_info
from ( from (
select c.id, c.authorized_override, select c.id, c.authorized_override,
greatest(0, date_part('day', c.suspended_until - now())) as days_suspended, greatest(0, date_part('day', c.suspended_until - now())) as days_suspended,
@ -516,7 +531,7 @@ function getCustomerById (id) {
c.front_camera_path, c.front_camera_override, c.front_camera_path, c.front_camera_override,
c.phone, c.sms_override, c.id_card_data, c.id_card_data_override, c.id_card_data_expiration, c.phone, c.sms_override, c.id_card_data, c.id_card_data_override, c.id_card_data_expiration,
c.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions, c.id_card_photo_path, c.id_card_photo_override, c.us_ssn, c.us_ssn_override, c.sanctions,
c.sanctions_at, c.sanctions_override, t.tx_class, t.fiat, t.fiat_code, t.created, c.sanctions_at, c.sanctions_override, c.subscriber_info, t.tx_class, t.fiat, t.fiat_code, t.created,
row_number() over (partition by c.id order by t.created desc) as rn, row_number() over (partition by c.id order by t.created desc) as rn,
sum(case when t.id is not null then 1 else 0 end) over (partition by c.id) as total_txs, sum(case when t.id is not null then 1 else 0 end) over (partition by c.id) as total_txs,
sum(case when error_code is null or error_code not in ($1^) then t.fiat else 0 end) over (partition by c.id) as total_spent sum(case when error_code is null or error_code not in ($1^) then t.fiat else 0 end) over (partition by c.id) as total_spent

View file

@ -28,6 +28,7 @@ const typeDef = gql`
lastTxFiatCode: String lastTxFiatCode: String
lastTxClass: String lastTxClass: String
transactions: [Transaction] transactions: [Transaction]
subscriberInfo: JSONObject
} }
input CustomerInput { input CustomerInput {
@ -53,6 +54,7 @@ const typeDef = gql`
lastTxFiatCode: String lastTxFiatCode: String
lastTxClass: String lastTxClass: String
suspendedUntil: Date suspendedUntil: Date
subscriberInfo: Boolean
} }
type Query { type Query {

View file

@ -1,8 +1,19 @@
const _ = require('lodash/fp') const _ = require('lodash/fp')
exports.NAME = 'MockSMS' const NAME = 'MockSMS'
exports.sendMessage = function sendMessage (account, rec) { function getLookup (account, number) {
console.log('Looking up number: %j', number)
return new Promise((resolve, reject) => {
if (_.endsWith('666', number)) {
reject (new Error(`${exports.NAME} mocked error!`))
} else {
setTimeout(resolve, 1)
}
})
}
function sendMessage (account, rec) {
console.log('Sending SMS: %j', rec) console.log('Sending SMS: %j', rec)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (_.endsWith('666', _.getOr(false, 'sms.toNumber', rec))) { if (_.endsWith('666', _.getOr(false, 'sms.toNumber', rec))) {
@ -12,3 +23,9 @@ exports.sendMessage = function sendMessage (account, rec) {
} }
}) })
} }
module.exports = {
NAME,
sendMessage,
getLookup
}

View file

@ -37,7 +37,27 @@ function sendMessage (account, rec) {
}) })
} }
function getLookup (account, number) {
return Promise.resolve()
.then(() => {
const client = twilio(account.accountSid, account.authToken)
return client.lookups.v1.phoneNumbers(number)
.fetch({ addOns: ['lamassu_ekata'] })
})
.then(info => info.addOns.results['lamassu_ekata'])
.catch(err => {
if (_.includes(err.code, BAD_NUMBER_CODES)) {
const badNumberError = new Error(err.message)
badNumberError.name = 'BadNumberError'
throw badNumberError
}
throw new Error(`Twilio error: ${err.message}`)
})
}
module.exports = { module.exports = {
NAME, NAME,
sendMessage sendMessage,
getLookup
} }

View file

@ -12,4 +12,15 @@ function sendMessage (settings, rec) {
}) })
} }
module.exports = {sendMessage} function getLookup (settings, number) {
return Promise.resolve()
.then(() => {
const pluginCode = argv.mockSms ? 'mock-sms' : 'twilio'
const plugin = ph.load(ph.SMS, pluginCode)
const account = settings.accounts[pluginCode]
return plugin.getLookup(account, number)
})
}
module.exports = { sendMessage, getLookup }

View file

@ -0,0 +1,15 @@
var db = require('./db')
exports.up = function (next) {
var sql = [
`ALTER TABLE customers ADD COLUMN subscriber_info JSON`,
`ALTER TABLE customers ADD COLUMN subscriber_info_at TIMESTAMPTZ`,
`ALTER TABLE customers ADD COLUMN subscriber_info_by UUID REFERENCES users(id)`
]
db.multi(sql, next)
}
exports.down = function (next) {
next()
}

View file

@ -97,6 +97,7 @@ const SET_CUSTOMER = gql`
lastTxFiat lastTxFiat
lastTxFiatCode lastTxFiatCode
lastTxClass lastTxClass
subscriberInfo
} }
} }
` `
@ -202,6 +203,24 @@ const CustomerProfile = memo(() => {
}> }>
{`${blocked ? 'Authorize' : 'Block'} customer`} {`${blocked ? 'Authorize' : 'Block'} customer`}
</ActionButton> </ActionButton>
<ActionButton
color="primary"
Icon={blocked ? AuthorizeIcon : BlockIcon}
InverseIcon={
blocked ? AuthorizeReversedIcon : BlockReversedIcon
}
onClick={() =>
setCustomer({
variables: {
customerId,
customerInput: {
subscriberInfo: true
}
}
})
}>
{`Retrieve information`}
</ActionButton>
</div> </div>
</div> </div>
)} )}