From 87a3b718db3a75c7a726f3e47f425fce0d302d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Tue, 18 Jan 2022 18:46:55 +0000 Subject: [PATCH] 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 --- .../resolvers/customInfoRequests.resolver.js | 6 +- .../graphql/types/customInfoRequests.type.js | 6 +- lib/new-admin/services/customInfoRequests.js | 26 +++-- lib/new-config-manager.js | 34 +++--- lib/routes/pollingRoutes.js | 6 +- ...42518884925-manual-custom-info-requests.js | 16 +++ .../src/pages/Customers/CustomerData.js | 6 +- .../src/pages/Customers/CustomerProfile.js | 8 +- .../components/CustomInfoRequestsData.js | 4 +- .../Triggers/components/AdvancedTriggers.js | 100 +++++++++++------- .../src/pages/Triggers/components/helper.js | 43 +++++--- 11 files changed, 167 insertions(+), 88 deletions(-) create mode 100644 migrations/1642518884925-manual-custom-info-requests.js diff --git a/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js b/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js index 897b415c..fefdcf6b 100644 --- a/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js +++ b/lib/new-admin/graphql/resolvers/customInfoRequests.resolver.js @@ -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) } } diff --git a/lib/new-admin/graphql/types/customInfoRequests.type.js b/lib/new-admin/graphql/types/customInfoRequests.type.js index 5a9ed909..917c9f6f 100644 --- a/lib/new-admin/graphql/types/customInfoRequests.type.js +++ b/lib/new-admin/graphql/types/customInfoRequests.type.js @@ -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 } ` diff --git a/lib/new-admin/services/customInfoRequests.js b/lib/new-admin/services/customInfoRequests.js index c19c112a..7c703443 100644 --- a/lib/new-admin/services/customInfoRequests.js +++ b/lib/new-admin/services/customInfoRequests.js @@ -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]) } diff --git a/lib/new-config-manager.js b/lib/new-config-manager.js index 531186ea..0e721736 100644 --- a/lib/new-config-manager.js +++ b/lib/new-config-manager.js @@ -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('_')) diff --git a/lib/routes/pollingRoutes.js b/lib/routes/pollingRoutes.js index b4089d54..c4568233 100644 --- a/lib/routes/pollingRoutes.js +++ b/lib/routes/pollingRoutes.js @@ -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 diff --git a/migrations/1642518884925-manual-custom-info-requests.js b/migrations/1642518884925-manual-custom-info-requests.js new file mode 100644 index 00000000..9a912c6d --- /dev/null +++ b/migrations/1642518884925-manual-custom-info-requests.js @@ -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() +} diff --git a/new-lamassu-admin/src/pages/Customers/CustomerData.js b/new-lamassu-admin/src/pages/Customers/CustomerData.js index 18a53cac..dde30bef 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerData.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerData.js @@ -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: , + 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 => { diff --git a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js index 91b076ee..b127b5ec 100644 --- a/new-lamassu-admin/src/pages/Customers/CustomerProfile.js +++ b/new-lamassu-admin/src/pages/Customers/CustomerProfile.js @@ -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 ) } ` diff --git a/new-lamassu-admin/src/pages/Customers/components/CustomInfoRequestsData.js b/new-lamassu-admin/src/pages/Customers/components/CustomInfoRequestsData.js index ced31cef..d8ca647d 100644 --- a/new-lamassu-admin/src/pages/Customers/components/CustomInfoRequestsData.js +++ b/new-lamassu-admin/src/pages/Customers/components/CustomInfoRequestsData.js @@ -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 ) } ` diff --git a/new-lamassu-admin/src/pages/Triggers/components/AdvancedTriggers.js b/new-lamassu-admin/src/pages/Triggers/components/AdvancedTriggers.js index cb58bfa2..038a6825 100644 --- a/new-lamassu-admin/src/pages/Triggers/components/AdvancedTriggers.js +++ b/new-lamassu-admin/src/pages/Triggers/components/AdvancedTriggers.js @@ -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 ( - <> -
- -
-
- -
- + !loading && ( + <> +
+ +
+
+ +
+ + ) ) }) diff --git a/new-lamassu-admin/src/pages/Triggers/components/helper.js b/new-lamassu-admin/src/pages/Triggers/components/helper.js index 0737bca8..e7c660df 100644 --- a/new-lamassu-admin/src/pages/Triggers/components/helper.js +++ b/new-lamassu-admin/src/pages/Triggers/components/helper.js @@ -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' }