fix: allow for custom info requests to be allowed or blocked

fix: improve custom info requests backend
fix: add custom info requests to trigger overrides
This commit is contained in:
Sérgio Salgado 2022-01-18 18:46:55 +00:00
parent 08bcf03a1e
commit 87a3b718db
11 changed files with 167 additions and 88 deletions

View file

@ -1,3 +1,4 @@
const authentication = require('../modules/userManagement')
const queries = require('../../services/customInfoRequests')
const DataLoader = require('dataloader')
@ -21,7 +22,10 @@ const resolvers = {
insertCustomInfoRequest: (...[, { customRequest }]) => queries.addCustomInfoRequest(customRequest),
removeCustomInfoRequest: (...[, { id }]) => queries.removeCustomInfoRequest(id),
editCustomInfoRequest: (...[, { id, customRequest }]) => queries.editCustomInfoRequest(id, customRequest),
setAuthorizedCustomRequest: (...[, { customerId, infoRequestId, isAuthorized }]) => queries.setAuthorizedCustomRequest(customerId, infoRequestId, isAuthorized),
setAuthorizedCustomRequest: (...[, { customerId, infoRequestId, override }, context]) => {
const token = authentication.getToken(context)
return queries.setAuthorizedCustomRequest(customerId, infoRequestId, override, token)
},
setCustomerCustomInfoRequest: (...[, { customerId, infoRequestId, data }]) => queries.setCustomerData(customerId, infoRequestId, data)
}
}

View file

@ -32,7 +32,9 @@ const typeDef = gql`
type CustomRequestData {
customerId: ID
infoRequestId: ID
approved: Boolean
override: String
overrideAt: Date
overrideBy: ID
customerData: JSON
customInfoRequest: CustomInfoRequest
}
@ -47,7 +49,7 @@ const typeDef = gql`
insertCustomInfoRequest(customRequest: CustomRequestInput!): CustomInfoRequest @auth
removeCustomInfoRequest(id: ID!): CustomInfoRequest @auth
editCustomInfoRequest(id: ID!, customRequest: CustomRequestInput!): CustomInfoRequest @auth
setAuthorizedCustomRequest(customerId: ID!, infoRequestId: ID!, isAuthorized: Boolean!): Boolean @auth
setAuthorizedCustomRequest(customerId: ID!, infoRequestId: ID!, override: String!): Boolean @auth
setCustomerCustomInfoRequest(customerId: ID!, infoRequestId: ID!, data: JSON!): Boolean @auth
}
`

View file

@ -35,8 +35,10 @@ const getAllCustomInfoRequestsForCustomer = (customerId) => {
return db.any(sql, [customerId]).then(res => res.map(item => ({
customerId: item.customer_id,
infoRequestId: item.info_request_id,
approved: item.approved,
customerData: item.customer_data
customerData: item.customer_data,
override: item.override,
overrideAt: item.override_at,
overrideBy: item.override_by
})))
}
@ -46,8 +48,10 @@ const getCustomInfoRequestForCustomer = (customerId, infoRequestId) => {
return {
customerId: item.customer_id,
infoRequestId: item.info_request_id,
approved: item.approved,
customerData: item.customer_data
customerData: item.customer_data,
override: item.override,
overrideAt: item.override_at,
overrideBy: item.override_by
}
})
}
@ -61,8 +65,10 @@ const batchGetAllCustomInfoRequestsForCustomer = (customerIds) => {
return items.map(item => ({
customerId: item.customer_id,
infoRequestId: item.info_request_id,
approved: item.approved,
customerData: item.customer_data
customerData: item.customer_data,
override: item.override,
overrideAt: item.override_at,
overrideBy: item.override_by
}))
})
})
@ -93,9 +99,9 @@ const batchGetCustomInfoRequest = (infoRequestIds) => {
})
}
const setAuthorizedCustomRequest = (customerId, infoRequestId, isAuthorized) => {
const sql = `UPDATE customers_custom_info_requests SET approved = $1 WHERE customer_id = $2 AND info_request_id = $3`
return db.none(sql, [isAuthorized, customerId, infoRequestId]).then(() => true)
const setAuthorizedCustomRequest = (customerId, infoRequestId, override, token) => {
const sql = `UPDATE customers_custom_info_requests SET override = $1, override_by = $2, override_at = now() WHERE customer_id = $3 AND info_request_id = $4`
return db.none(sql, [override, token, customerId, infoRequestId]).then(() => true)
}
const setCustomerData = (customerId, infoRequestId, data) => {
@ -103,7 +109,7 @@ const setCustomerData = (customerId, infoRequestId, data) => {
INSERT INTO customers_custom_info_requests (customer_id, info_request_id, customer_data)
VALUES ($1, $2, $3)
ON CONFLICT (customer_id, info_request_id)
DO UPDATE SET customer_data = $3, approved = null`
DO UPDATE SET customer_data = $3`
return db.none(sql, [customerId, infoRequestId, data])
}

View file

@ -1,4 +1,5 @@
const _ = require('lodash/fp')
const { getCustomInfoRequests } = require('./new-admin/services/customInfoRequests')
const namespaces = {
WALLETS: 'wallets',
@ -107,22 +108,29 @@ const getGlobalNotifications = config => getNotifications(null, null, config)
const getTriggers = _.get('triggers')
const getTriggersAutomation = config => {
const defaultAutomation = _.get('triggersConfig_automation')(config)
const requirements = {
sanctions: defaultAutomation,
idCardPhoto: defaultAutomation,
idCardData: defaultAutomation,
facephoto: defaultAutomation,
usSsn: defaultAutomation
}
return getCustomInfoRequests(true)
.then(infoRequests => {
const defaultAutomation = _.get('triggersConfig_automation')(config)
const requirements = {
sanctions: defaultAutomation,
idCardPhoto: defaultAutomation,
idCardData: defaultAutomation,
facephoto: defaultAutomation,
usSsn: defaultAutomation
}
const overrides = _.get('triggersConfig_overrides')(config)
_.forEach(it => {
requirements[it.id] = defaultAutomation
}, infoRequests)
const requirementsOverrides = _.reduce((acc, override) => {
return _.assign(acc, { [override.requirement]: override.automation })
}, {}, overrides)
const overrides = _.get('triggersConfig_overrides')(config)
return _.assign(requirements, requirementsOverrides)
const requirementsOverrides = _.reduce((acc, override) => {
return _.assign(acc, { [override.requirement]: override.automation })
}, {}, overrides)
return _.assign(requirements, requirementsOverrides)
})
}
const splitGetFirst = _.compose(_.head, _.split('_'))

View file

@ -73,7 +73,7 @@ function poll (req, res, next) {
const pi = plugins(settings, deviceId)
const hasLightning = checkHasLightning(settings)
const triggersAutomation = configManager.getTriggersAutomation(settings.config)
const triggersAutomationPromise = configManager.getTriggersAutomation(settings.config)
const triggersPromise = buildTriggers(configManager.getTriggers(settings.config))
const operatorInfo = configManager.getOperatorInfo(settings.config)
@ -84,8 +84,8 @@ function poll (req, res, next) {
state.pids[operatorId] = { [deviceId]: { pid, ts: Date.now() } }
return Promise.all([pi.pollQueries(serialNumber, deviceTime, req.query, machineVersion, machineModel), triggersPromise])
.then(([results, triggers]) => {
return Promise.all([pi.pollQueries(serialNumber, deviceTime, req.query, machineVersion, machineModel), triggersPromise, triggersAutomationPromise])
.then(([results, triggers, triggersAutomation]) => {
const cassettes = results.cassettes
const reboot = pid && state.reboots?.[operatorId]?.[deviceId] === pid

View file

@ -0,0 +1,16 @@
var db = require('./db')
exports.up = function (next) {
var sql = [
`ALTER TABLE customers_custom_info_requests DROP COLUMN approved`,
`ALTER TABLE customers_custom_info_requests ADD COLUMN override verification_type NOT NULL DEFAULT 'automatic'`,
`ALTER TABLE customers_custom_info_requests ADD COLUMN override_by UUID REFERENCES users(id)`,
`ALTER TABLE customers_custom_info_requests ADD COLUMN override_at TIMESTAMPTZ`
]
db.multi(sql, next)
}
exports.down = function (next) {
next()
}

View file

@ -324,7 +324,6 @@ const CustomerData = ({
]
R.forEach(it => {
console.log('it', it)
customRequirements.push({
fields: [
{
@ -336,12 +335,13 @@ const CustomerData = ({
],
title: it.customInfoRequest.customRequest.name,
titleIcon: <CardIcon className={classes.cardIcon} />,
state: R.path(['override'])(it),
authorize: () =>
authorizeCustomRequest({
variables: {
customerId: it.customerId,
infoRequestId: it.customInfoRequest.id,
isAuthorized: true
override: OVERRIDE_AUTHORIZED
}
}),
reject: () =>
@ -349,7 +349,7 @@ const CustomerData = ({
variables: {
customerId: it.customerId,
infoRequestId: it.customInfoRequest.id,
isAuthorized: false
override: OVERRIDE_REJECTED
}
}),
save: values => {

View file

@ -95,7 +95,9 @@ const GET_CUSTOMER = gql`
}
customInfoRequests {
customerId
approved
override
overrideBy
overrideAt
customerData
customInfoRequest {
id
@ -180,12 +182,12 @@ const SET_AUTHORIZED_REQUEST = gql`
mutation setAuthorizedCustomRequest(
$customerId: ID!
$infoRequestId: ID!
$isAuthorized: Boolean!
$override: String!
) {
setAuthorizedCustomRequest(
customerId: $customerId
infoRequestId: $infoRequestId
isAuthorized: $isAuthorized
override: $override
)
}
`

View file

@ -53,12 +53,12 @@ const SET_AUTHORIZED_REQUEST = gql`
mutation setAuthorizedCustomRequest(
$customerId: ID!
$infoRequestId: ID!
$isAuthorized: Boolean!
$override: String!
) {
setAuthorizedCustomRequest(
customerId: $customerId
infoRequestId: $infoRequestId
isAuthorized: $isAuthorized
override: $override
)
}
`

View file

@ -28,13 +28,34 @@ const GET_INFO = gql`
}
`
const GET_CUSTOM_REQUESTS = gql`
query customInfoRequests {
customInfoRequests {
id
customRequest
enabled
}
}
`
const AdvancedTriggersSettings = memo(() => {
const SCREEN_KEY = namespaces.TRIGGERS
const [error, setError] = useState(null)
const [isEditingDefault, setEditingDefault] = useState(false)
const [isEditingOverrides, setEditingOverrides] = useState(false)
const { data } = useQuery(GET_INFO)
const { data, loading: configLoading } = useQuery(GET_INFO)
const { data: customInfoReqData, loading: customInfoLoading } = useQuery(
GET_CUSTOM_REQUESTS
)
const customInfoRequests =
R.path(['customInfoRequests'])(customInfoReqData) ?? []
const enabledCustomInfoRequests = R.filter(R.propEq('enabled', true))(
customInfoRequests
)
const loading = configLoading || customInfoLoading
const [saveConfig] = useMutation(SAVE_CONFIG, {
refetchQueries: () => ['getData'],
@ -67,42 +88,47 @@ const AdvancedTriggersSettings = memo(() => {
const onEditingOverrides = (it, editing) => setEditingOverrides(editing)
return (
<>
<Section>
<EditableTable
title="Default requirement settings"
error={error?.message}
titleLg
name="triggersConfig"
enableEdit
initialValues={requirementsDefaults}
save={saveDefaults}
validationSchema={defaultSchema}
data={R.of(requirementsDefaults)}
elements={getDefaultSettings()}
setEditing={onEditingDefault}
forceDisable={isEditingOverrides}
/>
</Section>
<Section>
<EditableTable
error={error?.message}
title="Overrides"
titleLg
name="overrides"
enableDelete
enableEdit
enableCreate
initialValues={overridesDefaults}
save={saveOverrides}
validationSchema={getOverridesSchema(requirementsOverrides)}
data={requirementsOverrides}
elements={getOverrides()}
setEditing={onEditingOverrides}
forceDisable={isEditingDefault}
/>
</Section>
</>
!loading && (
<>
<Section>
<EditableTable
title="Default requirement settings"
error={error?.message}
titleLg
name="triggersConfig"
enableEdit
initialValues={requirementsDefaults}
save={saveDefaults}
validationSchema={defaultSchema}
data={R.of(requirementsDefaults)}
elements={getDefaultSettings()}
setEditing={onEditingDefault}
forceDisable={isEditingOverrides}
/>
</Section>
<Section>
<EditableTable
error={error?.message}
title="Overrides"
titleLg
name="overrides"
enableDelete
enableEdit
enableCreate
initialValues={overridesDefaults}
save={saveOverrides}
validationSchema={getOverridesSchema(
requirementsOverrides,
enabledCustomInfoRequests
)}
data={requirementsOverrides}
elements={getOverrides(enabledCustomInfoRequests)}
setEditing={onEditingOverrides}
forceDisable={isEditingDefault}
/>
</Section>
</>
)
)
})

View file

@ -4,18 +4,29 @@ import * as Yup from 'yup'
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
import { getView } from 'src/pages/Triggers/helper'
const advancedRequirementOptions = [
{ display: 'Sanctions', code: 'sanctions' },
{ display: 'ID card image', code: 'idCardPhoto' },
{ display: 'ID data', code: 'idCardData' },
{ display: 'Customer camera', code: 'facephoto' },
{ display: 'US SSN', code: 'usSsn' }
]
const buildAdvancedRequirementOptions = customInfoRequests => {
const base = [
{ display: 'Sanctions', code: 'sanctions' },
{ display: 'ID card image', code: 'idCardPhoto' },
{ display: 'ID data', code: 'idCardData' },
{ display: 'Customer camera', code: 'facephoto' },
{ display: 'US SSN', code: 'usSsn' }
]
const displayRequirement = code => {
const custom = R.map(it => ({
display: it.customRequest.name,
code: it.id
}))(customInfoRequests)
return R.concat(base, custom)
}
const displayRequirement = (code, customInfoRequests) => {
return R.prop(
'display',
R.find(R.propEq('code', code))(advancedRequirementOptions)
R.find(R.propEq('code', code))(
buildAdvancedRequirementOptions(customInfoRequests)
)
)
}
@ -29,7 +40,7 @@ const defaultSchema = Yup.object().shape({
.required()
})
const getOverridesSchema = values => {
const getOverridesSchema = (values, customInfoRequests) => {
return Yup.object().shape({
id: Yup.string()
.label('Requirement')
@ -40,7 +51,8 @@ const getOverridesSchema = values => {
if (R.find(R.propEq('requirement', requirement))(values)) {
return this.createError({
message: `Requirement ${displayRequirement(
requirement
requirement,
customInfoRequests
)} already overriden`
})
}
@ -84,17 +96,20 @@ const getDefaultSettings = () => {
]
}
const getOverrides = () => {
const getOverrides = customInfoRequests => {
return [
{
name: 'requirement',
header: 'Requirement',
width: 196,
size: 'sm',
view: getView(advancedRequirementOptions, 'display'),
view: getView(
buildAdvancedRequirementOptions(customInfoRequests),
'display'
),
input: Autocomplete,
inputProps: {
options: advancedRequirementOptions,
options: buildAdvancedRequirementOptions(customInfoRequests),
labelProp: 'display',
valueProp: 'code'
}