Merge pull request #1055 from chaotixkilla/feat-custom-info-requests-as-manual-triggers

Add custom info request manual trigger behavior
This commit is contained in:
Rafael Taranto 2022-01-19 14:37:58 +00:00 committed by GitHub
commit d5b7b088cd
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 queries = require('../../services/customInfoRequests')
const DataLoader = require('dataloader') const DataLoader = require('dataloader')
@ -21,7 +22,10 @@ const resolvers = {
insertCustomInfoRequest: (...[, { customRequest }]) => queries.addCustomInfoRequest(customRequest), insertCustomInfoRequest: (...[, { customRequest }]) => queries.addCustomInfoRequest(customRequest),
removeCustomInfoRequest: (...[, { id }]) => queries.removeCustomInfoRequest(id), removeCustomInfoRequest: (...[, { id }]) => queries.removeCustomInfoRequest(id),
editCustomInfoRequest: (...[, { id, customRequest }]) => queries.editCustomInfoRequest(id, customRequest), 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) setCustomerCustomInfoRequest: (...[, { customerId, infoRequestId, data }]) => queries.setCustomerData(customerId, infoRequestId, data)
} }
} }

View file

@ -32,7 +32,9 @@ const typeDef = gql`
type CustomRequestData { type CustomRequestData {
customerId: ID customerId: ID
infoRequestId: ID infoRequestId: ID
approved: Boolean override: String
overrideAt: Date
overrideBy: ID
customerData: JSON customerData: JSON
customInfoRequest: CustomInfoRequest customInfoRequest: CustomInfoRequest
} }
@ -47,7 +49,7 @@ const typeDef = gql`
insertCustomInfoRequest(customRequest: CustomRequestInput!): CustomInfoRequest @auth insertCustomInfoRequest(customRequest: CustomRequestInput!): CustomInfoRequest @auth
removeCustomInfoRequest(id: ID!): CustomInfoRequest @auth removeCustomInfoRequest(id: ID!): CustomInfoRequest @auth
editCustomInfoRequest(id: ID!, customRequest: CustomRequestInput!): 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 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 => ({ return db.any(sql, [customerId]).then(res => res.map(item => ({
customerId: item.customer_id, customerId: item.customer_id,
infoRequestId: item.info_request_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 { return {
customerId: item.customer_id, customerId: item.customer_id,
infoRequestId: item.info_request_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 => ({ return items.map(item => ({
customerId: item.customer_id, customerId: item.customer_id,
infoRequestId: item.info_request_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 setAuthorizedCustomRequest = (customerId, infoRequestId, override, token) => {
const sql = `UPDATE customers_custom_info_requests SET approved = $1 WHERE customer_id = $2 AND info_request_id = $3` 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, [isAuthorized, customerId, infoRequestId]).then(() => true) return db.none(sql, [override, token, customerId, infoRequestId]).then(() => true)
} }
const setCustomerData = (customerId, infoRequestId, data) => { 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) INSERT INTO customers_custom_info_requests (customer_id, info_request_id, customer_data)
VALUES ($1, $2, $3) VALUES ($1, $2, $3)
ON CONFLICT (customer_id, info_request_id) 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]) return db.none(sql, [customerId, infoRequestId, data])
} }

View file

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

View file

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

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

View file

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

View file

@ -53,12 +53,12 @@ const SET_AUTHORIZED_REQUEST = gql`
mutation setAuthorizedCustomRequest( mutation setAuthorizedCustomRequest(
$customerId: ID! $customerId: ID!
$infoRequestId: ID! $infoRequestId: ID!
$isAuthorized: Boolean! $override: String!
) { ) {
setAuthorizedCustomRequest( setAuthorizedCustomRequest(
customerId: $customerId customerId: $customerId
infoRequestId: $infoRequestId 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 AdvancedTriggersSettings = memo(() => {
const SCREEN_KEY = namespaces.TRIGGERS const SCREEN_KEY = namespaces.TRIGGERS
const [error, setError] = useState(null) const [error, setError] = useState(null)
const [isEditingDefault, setEditingDefault] = useState(false) const [isEditingDefault, setEditingDefault] = useState(false)
const [isEditingOverrides, setEditingOverrides] = 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, { const [saveConfig] = useMutation(SAVE_CONFIG, {
refetchQueries: () => ['getData'], refetchQueries: () => ['getData'],
@ -67,42 +88,47 @@ const AdvancedTriggersSettings = memo(() => {
const onEditingOverrides = (it, editing) => setEditingOverrides(editing) const onEditingOverrides = (it, editing) => setEditingOverrides(editing)
return ( return (
<> !loading && (
<Section> <>
<EditableTable <Section>
title="Default requirement settings" <EditableTable
error={error?.message} title="Default requirement settings"
titleLg error={error?.message}
name="triggersConfig" titleLg
enableEdit name="triggersConfig"
initialValues={requirementsDefaults} enableEdit
save={saveDefaults} initialValues={requirementsDefaults}
validationSchema={defaultSchema} save={saveDefaults}
data={R.of(requirementsDefaults)} validationSchema={defaultSchema}
elements={getDefaultSettings()} data={R.of(requirementsDefaults)}
setEditing={onEditingDefault} elements={getDefaultSettings()}
forceDisable={isEditingOverrides} setEditing={onEditingDefault}
/> forceDisable={isEditingOverrides}
</Section> />
<Section> </Section>
<EditableTable <Section>
error={error?.message} <EditableTable
title="Overrides" error={error?.message}
titleLg title="Overrides"
name="overrides" titleLg
enableDelete name="overrides"
enableEdit enableDelete
enableCreate enableEdit
initialValues={overridesDefaults} enableCreate
save={saveOverrides} initialValues={overridesDefaults}
validationSchema={getOverridesSchema(requirementsOverrides)} save={saveOverrides}
data={requirementsOverrides} validationSchema={getOverridesSchema(
elements={getOverrides()} requirementsOverrides,
setEditing={onEditingOverrides} enabledCustomInfoRequests
forceDisable={isEditingDefault} )}
/> data={requirementsOverrides}
</Section> 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 Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
import { getView } from 'src/pages/Triggers/helper' import { getView } from 'src/pages/Triggers/helper'
const advancedRequirementOptions = [ const buildAdvancedRequirementOptions = customInfoRequests => {
{ display: 'Sanctions', code: 'sanctions' }, const base = [
{ display: 'ID card image', code: 'idCardPhoto' }, { display: 'Sanctions', code: 'sanctions' },
{ display: 'ID data', code: 'idCardData' }, { display: 'ID card image', code: 'idCardPhoto' },
{ display: 'Customer camera', code: 'facephoto' }, { display: 'ID data', code: 'idCardData' },
{ display: 'US SSN', code: 'usSsn' } { 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( return R.prop(
'display', '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() .required()
}) })
const getOverridesSchema = values => { const getOverridesSchema = (values, customInfoRequests) => {
return Yup.object().shape({ return Yup.object().shape({
id: Yup.string() id: Yup.string()
.label('Requirement') .label('Requirement')
@ -40,7 +51,8 @@ const getOverridesSchema = values => {
if (R.find(R.propEq('requirement', requirement))(values)) { if (R.find(R.propEq('requirement', requirement))(values)) {
return this.createError({ return this.createError({
message: `Requirement ${displayRequirement( message: `Requirement ${displayRequirement(
requirement requirement,
customInfoRequests
)} already overriden` )} already overriden`
}) })
} }
@ -84,17 +96,20 @@ const getDefaultSettings = () => {
] ]
} }
const getOverrides = () => { const getOverrides = customInfoRequests => {
return [ return [
{ {
name: 'requirement', name: 'requirement',
header: 'Requirement', header: 'Requirement',
width: 196, width: 196,
size: 'sm', size: 'sm',
view: getView(advancedRequirementOptions, 'display'), view: getView(
buildAdvancedRequirementOptions(customInfoRequests),
'display'
),
input: Autocomplete, input: Autocomplete,
inputProps: { inputProps: {
options: advancedRequirementOptions, options: buildAdvancedRequirementOptions(customInfoRequests),
labelProp: 'display', labelProp: 'display',
valueProp: 'code' valueProp: 'code'
} }