fix: use gql upload lib and remove logs

This commit is contained in:
José Oliveira 2021-11-09 13:55:12 +00:00
parent 3e4f4a4962
commit 68c635ce38
10 changed files with 112 additions and 70 deletions

View file

@ -161,24 +161,20 @@ function edit (id, data, userToken) {
] ]
const filteredData = _.pick(defaults, _.mapKeys(_.snakeCase, _.omitBy(_.isNil, data))) const filteredData = _.pick(defaults, _.mapKeys(_.snakeCase, _.omitBy(_.isNil, data)))
if (_.isEmpty(filteredData)) return getCustomerById(id) if (_.isEmpty(filteredData)) return getCustomerById(id)
if (filteredData.front_camera_data || filteredData.id_card_photo_data) return getCustomerById(id) const formattedData = enhanceEditedPhotos(enhanceEditedFields(filteredData, userToken))
const formattedData = enhanceEditedFields(filteredData, userToken)
const defaultDbData = { const defaultDbData = {
customer_id: id, customer_id: id,
created: new Date(), created: new Date(),
...formattedData ...formattedData
} }
console.log(formattedData, 'FORMATED', defaultDbData, 'DEFAULT DB')
const cs = new Pgp.helpers.ColumnSet(_.keys(defaultDbData), const cs = new Pgp.helpers.ColumnSet(_.keys(defaultDbData),
{ table: 'edited_customer_data' }) { table: 'edited_customer_data' })
const onConflict = ' ON CONFLICT (customer_id) DO UPDATE SET ' + const onConflict = ' ON CONFLICT (customer_id) DO UPDATE SET ' +
cs.assignColumns({ from: 'EXCLUDED', skip: ['customer_id', 'created'] }) cs.assignColumns({ from: 'EXCLUDED', skip: ['customer_id', 'created'] })
const upsert = Pgp.helpers.insert(defaultDbData, cs) + onConflict const upsert = Pgp.helpers.insert(defaultDbData, cs) + onConflict
db.none(upsert) return db.none(upsert)
.then(res => { .then(getCustomerById(id))
console.log(res)
return getCustomerById(id)
})
} }
/** /**
@ -202,6 +198,25 @@ function enhanceEditedFields (fields, userToken) {
return fields return fields
} }
/**
* Add *_path to edited photos fields
*
* @name enhanceEditedFields
* @function
*
* @param {object} fields Fields to be enhanced
* @returns {object} fields enhanced with *_path
*/
function enhanceEditedPhotos (fields) {
return _.mapKeys((field) => {
if (_.includes(field, ['front_camera', 'id_card_photo'])) {
return field + '_path'
}
return field
}, fields)
}
/** /**
* Remove the edited data from the db record * Remove the edited data from the db record
* *
@ -248,38 +263,27 @@ function deleteEditedData (id, data) {
* @returns {object} path New photo path * @returns {object} path New photo path
* *
*/ */
function replacePhoto (id, photo, photoType) { async function updateEditedPhoto (id, photo, photoType) {
const newPatch = {}
const baseDir = photoType === 'frontCamera' ? frontCameraBaseDir : idPhotoCardBasedir const baseDir = photoType === 'frontCamera' ? frontCameraBaseDir : idPhotoCardBasedir
const { createReadStream } = photo const { createReadStream, filename } = photo
const stream = createReadStream() const stream = createReadStream()
// workout the image hash const randomString = uuid.v4().toString() + '/'
// i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50
const hash = crypto
.createHash('sha256')
.update(imageData)
.digest('hex')
// workout the image folder // i.e. ..62ed29c5-f37e-4fb7-95bb-c52d4a3738f7/filename.jpg
// i.e. 24/0e/85 const rpath = path.join(randomString, filename)
const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash)))))
// i.e. ../<lamassu-server-home>/idphotocard/24/0e/85
const dirname = path.join(idPhotoCardBasedir, rpath)
// create the directory tree if needed // create the directory tree if needed
_.attempt(() => makeDir.sync(dirname)) _.attempt(() => makeDir.sync(path.join(baseDir, randomString)))
// i.e. ../<lamassu-server-home>/idphotocard/24/0e/85/240e85ff2e4bb931f235985dd01....jpg // i.e. ../<lamassu-server-home>/idphotocard/62ed29c5-f37e-4fb7-95bb-c52d4a3738f7/filename.jpg
const filename = path.join(dirname, hash + '.jpg') const pathName = path.join(baseDir, rpath)
// update db record patch await stream.pipe(fs.createWriteStream(pathName))
// i.e. { newPatch[photoType] = rpath
// "idCardPhotoPath": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg",
// "idCardPhotoAt": "now()" return newPatch
// }
newPatch.idCardPhotoPath = path.join(rpath, hash + '.jpg')
newPatch.idCardPhotoAt = 'now()'
} }
const invalidateCustomerNotifications = (id, data) => { const invalidateCustomerNotifications = (id, data) => {
@ -670,9 +674,9 @@ function getCustomersList (phone = null, name = null, address = null, id = null)
*/ */
function getCustomerById (id) { function getCustomerById (id) {
const passableErrorCodes = _.map(Pgp.as.text, TX_PASSTHROUGH_ERROR_CODES).join(',') const passableErrorCodes = _.map(Pgp.as.text, TX_PASSTHROUGH_ERROR_CODES).join(',')
const sql = `select id, authorized_override, days_suspended, is_suspended, front_camera_path, front_camera_at, front_camera_override, const sql = `select id, authorized_override, days_suspended, is_suspended, front_camera_at, front_camera_path, front_camera_at, front_camera_override,
phone, sms_override, id_card_data, id_card_data_override, id_card_data_expiration, phone, sms_override, id_card_data_at, 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_at, id_card_photo_path, id_card_photo_override, us_ssn_at, 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, subscriber_info, custom_fields fiat_code as last_tx_fiat_code, tx_class as last_tx_class, subscriber_info, custom_fields
from ( from (
@ -680,8 +684,8 @@ function getCustomerById (id) {
greatest(0, date_part('day', c.suspended_until - now())) as days_suspended, greatest(0, date_part('day', c.suspended_until - now())) as days_suspended,
c.suspended_until > now() as is_suspended, c.suspended_until > now() as is_suspended,
c.front_camera_path, c.front_camera_at, c.front_camera_override, c.front_camera_path, c.front_camera_at, 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_at, 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_at, c.id_card_photo_override, c.us_ssn, c.us_ssn_at, c.us_ssn_override, c.sanctions,
c.sanctions_at, c.sanctions_override, c.subscriber_info, 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,
@ -700,6 +704,10 @@ function getCustomerById (id) {
where c.id = $2 where c.id = $2
) as cl where rn = 1` ) as cl where rn = 1`
return db.oneOrNone(sql, [passableErrorCodes, id]) return db.oneOrNone(sql, [passableErrorCodes, id])
.then(customerData => {
return getEditedData(id)
.then(customerEditedData => selectLatestData(customerData, customerEditedData))
})
.then(populateOverrideUsernames) .then(populateOverrideUsernames)
.then(camelize) .then(camelize)
} }
@ -709,10 +717,16 @@ function getCustomerById (id) {
* *
* @param {String} id customer id * @param {String} id customer id
* *
* @returns {array} A single customer instance with the most recent data * @returns {array} A single customer instance with the most recent edited data
*/ */
function getManuallyEditedData (id) { function getEditedData (id) {
const sql = `SELECT * FROM edited_customer_data WHERE customer_id = $1`
return db.oneOrNone(sql, [id])
.then(_.omitBy(_.isNil))
}
function selectLatestData (customerData, customerEditedData) {
return customerData
} }
/** /**
@ -983,7 +997,7 @@ module.exports = {
saveCustomField, saveCustomField,
removeCustomField, removeCustomField,
edit, edit,
getManuallyEditedData,
deleteEditedData, deleteEditedData,
updateEditedPhoto,
updateTxCustomerPhoto updateTxCustomerPhoto
} }

View file

@ -9,7 +9,7 @@ const helmet = require('helmet')
const nocache = require('nocache') const nocache = require('nocache')
const cookieParser = require('cookie-parser') const cookieParser = require('cookie-parser')
const { ApolloServer, AuthenticationError } = require('apollo-server-express') const { ApolloServer, AuthenticationError } = require('apollo-server-express')
// const { graphqlUploadExpress } = require('graphql-upload') const { graphqlUploadExpress } = require('graphql-upload')
const _ = require('lodash/fp') const _ = require('lodash/fp')
const { asyncLocalStorage, defaultStore } = require('../async-storage') const { asyncLocalStorage, defaultStore } = require('../async-storage')
@ -48,11 +48,12 @@ app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL))
app.use(computeSchema) app.use(computeSchema)
app.use(findOperatorId) app.use(findOperatorId)
app.use(session) app.use(session)
// app.use(graphqlUploadExpress()) app.use(graphqlUploadExpress())
const apolloServer = new ApolloServer({ const apolloServer = new ApolloServer({
typeDefs, typeDefs,
resolvers, resolvers,
uploads: false,
schemaDirectives: { schemaDirectives: {
auth: AuthDirective auth: AuthDirective
}, },

View file

@ -27,9 +27,10 @@ const resolvers = {
return customers.edit(customerId, editedData, token) return customers.edit(customerId, editedData, token)
}, },
replacePhoto: async (root, { customerId, photoType, newPhoto }, context) => { replacePhoto: async (root, { customerId, photoType, newPhoto }, context) => {
const token = !!context.req.cookies.lid && context.req.session.user.id
const photo = await newPhoto const photo = await newPhoto
return customers.replacePhoto(customerId, photoType, photo) return customers.updateEditedPhoto(customerId, photo, photoType)
.then(() => customers.getCustomerById(customerId)) .then(newPatch => customers.edit(customerId, newPatch, token))
}, },
deleteEditedData: (root, { customerId, customerEdit }) => { deleteEditedData: (root, { customerId, customerEdit }) => {
return customers.deleteEditedData(customerId, customerEdit) return customers.deleteEditedData(customerId, customerEdit)

View file

@ -1,12 +1,13 @@
const { GraphQLDateTime } = require('graphql-iso-date') const { GraphQLDateTime } = require('graphql-iso-date')
const { GraphQLJSON, GraphQLJSONObject } = require('graphql-type-json') const { GraphQLJSON, GraphQLJSONObject } = require('graphql-type-json')
const { GraphQLUpload } = require('graphql-upload')
GraphQLDateTime.name = 'Date' GraphQLDateTime.name = 'Date'
const resolvers = { const resolvers = {
JSON: GraphQLJSON, JSON: GraphQLJSON,
JSONObject: GraphQLJSONObject, JSONObject: GraphQLJSONObject,
Date: GraphQLDateTime Date: GraphQLDateTime,
UploadGQL: GraphQLUpload
} }
module.exports = resolvers module.exports = resolvers

View file

@ -12,7 +12,7 @@ const typeDef = gql`
authorizedOverride: String authorizedOverride: String
daysSuspended: Int daysSuspended: Int
isSuspended: Boolean isSuspended: Boolean
newPhoto: Upload newPhoto: UploadGQL
photoType: String photoType: String
frontCameraPath: String frontCameraPath: String
frontCameraAt: Date frontCameraAt: Date
@ -23,7 +23,7 @@ const typeDef = gql`
idCardData: JSONObject idCardData: JSONObject
idCardDataOverride: String idCardDataOverride: String
idCardDataExpiration: Date idCardDataExpiration: Date
idCardPhoto: Upload idCardPhoto: UploadGQL
idCardPhotoPath: String idCardPhotoPath: String
idCardPhotoOverride: String idCardPhotoOverride: String
usSsn: String usSsn: String
@ -70,7 +70,7 @@ const typeDef = gql`
input CustomerEdit { input CustomerEdit {
idCardData: JSONObject idCardData: JSONObject
idCardPhoto: Upload idCardPhoto: UploadGQL
usSsn: String usSsn: String
} }
@ -87,7 +87,7 @@ const typeDef = gql`
removeCustomField(customerId: ID!, fieldId: ID!): CustomerCustomField @auth removeCustomField(customerId: ID!, fieldId: ID!): CustomerCustomField @auth
editCustomer(customerId: ID!, customerEdit: CustomerEdit): Customer @auth editCustomer(customerId: ID!, customerEdit: CustomerEdit): Customer @auth
deleteEditedData(customerId: ID!, customerEdit: CustomerEdit): Customer @auth deleteEditedData(customerId: ID!, customerEdit: CustomerEdit): Customer @auth
replacePhoto(customerId: ID!, photoType: String, newPhoto: Upload): Customer @auth replacePhoto(customerId: ID!, photoType: String, newPhoto: UploadGQL): Customer @auth
} }
` `

View file

@ -4,6 +4,7 @@ const typeDef = gql`
scalar JSON scalar JSON
scalar JSONObject scalar JSONObject
scalar Date scalar Date
scalar UploadGQL
` `
module.exports = typeDef module.exports = typeDef

View file

@ -85,13 +85,7 @@ const CustomerData = ({
const isEven = elem => elem % 2 === 0 const isEven = elem => elem % 2 === 0
const getVisibleCards = _.filter( const getVisibleCards = _.filter(elem => elem.isAvailable)
elem =>
!_.isEmpty(elem.fields) ||
(!_.isNil(elem.children) && !_.isNil(elem.state))
)
const getAvailableFields = _.filter(({ value }) => value !== '')
const schemas = { const schemas = {
idScan: Yup.object().shape({ idScan: Yup.object().shape({
@ -188,7 +182,7 @@ const CustomerData = ({
const cards = [ const cards = [
{ {
fields: getAvailableFields(idScanElements), fields: idScanElements,
title: 'ID Scan', title: 'ID Scan',
titleIcon: <PhoneIcon className={classes.cardIcon} />, titleIcon: <PhoneIcon className={classes.cardIcon} />,
state: R.path(['idCardDataOverride'])(customer), state: R.path(['idCardDataOverride'])(customer),
@ -198,21 +192,24 @@ const CustomerData = ({
deleteEditedData: () => deleteEditedData({ idCardData: null }), deleteEditedData: () => deleteEditedData({ idCardData: null }),
save: values => editCustomer({ idCardData: values }), save: values => editCustomer({ idCardData: values }),
validationSchema: schemas.idScan, validationSchema: schemas.idScan,
initialValues: initialValues.idScan initialValues: initialValues.idScan,
isAvailable: !_.isNil(idData)
}, },
{ {
title: 'SMS Confirmation', title: 'SMS Confirmation',
titleIcon: <CardIcon className={classes.cardIcon} />, titleIcon: <CardIcon className={classes.cardIcon} />,
authorize: () => {}, authorize: () => {},
reject: () => {}, reject: () => {},
save: () => {} save: () => {},
isAvailable: false
}, },
{ {
title: 'Name', title: 'Name',
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon className={classes.editIcon} />,
authorize: () => {}, authorize: () => {},
reject: () => {}, reject: () => {},
save: () => {} save: () => {},
isAvailable: false
}, },
{ {
title: 'Sanctions check', title: 'Sanctions check',
@ -221,10 +218,11 @@ const CustomerData = ({
authorize: () => authorize: () =>
updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }), updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }),
reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }), reject: () => updateCustomer({ sanctionsOverride: OVERRIDE_REJECTED }),
children: <Info3>{sanctionsDisplay}</Info3> children: <Info3>{sanctionsDisplay}</Info3>,
isAvailable: !_.isNil(sanctions)
}, },
{ {
fields: getAvailableFields(frontCameraElements), fields: frontCameraElements,
title: 'Front facing camera', title: 'Front facing camera',
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon className={classes.editIcon} />,
state: R.path(['frontCameraOverride'])(customer), state: R.path(['frontCameraOverride'])(customer),
@ -247,10 +245,11 @@ const CustomerData = ({
) : null, ) : null,
hasImage: true, hasImage: true,
validationSchema: schemas.frontCamera, validationSchema: schemas.frontCamera,
initialValues: initialValues.frontCamera initialValues: initialValues.frontCamera,
isAvailable: !_.isNil(customer.frontCameraPath)
}, },
{ {
fields: getAvailableFields(idCardPhotoElements), fields: idCardPhotoElements,
title: 'ID card image', title: 'ID card image',
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon className={classes.editIcon} />,
state: R.path(['idCardPhotoOverride'])(customer), state: R.path(['idCardPhotoOverride'])(customer),
@ -271,10 +270,11 @@ const CustomerData = ({
) : null, ) : null,
hasImage: true, hasImage: true,
validationSchema: schemas.idCardPhoto, validationSchema: schemas.idCardPhoto,
initialValues: initialValues.idCardPhoto initialValues: initialValues.idCardPhoto,
isAvailable: !_.isNil(customer.idCardPhotoPath)
}, },
{ {
fields: getAvailableFields(usSsnElements), fields: usSsnElements,
title: 'US SSN', title: 'US SSN',
titleIcon: <CardIcon className={classes.cardIcon} />, titleIcon: <CardIcon className={classes.cardIcon} />,
state: R.path(['usSsnOverride'])(customer), state: R.path(['usSsnOverride'])(customer),
@ -283,7 +283,8 @@ const CustomerData = ({
save: values => editCustomer({ usSsn: values.usSsn }), save: values => editCustomer({ usSsn: values.usSsn }),
deleteEditedData: () => deleteEditedData({ usSsn: null }), deleteEditedData: () => deleteEditedData({ usSsn: null }),
validationSchema: schemas.usSsn, validationSchema: schemas.usSsn,
initialValues: initialValues.usSsn initialValues: initialValues.usSsn,
isAvailable: !_.isNil(customer.usSsn)
} }
] ]

View file

@ -75,7 +75,6 @@ const fieldUseStyles = makeStyles(fieldStyles)
const EditableField = ({ editing, field, value, size, ...props }) => { const EditableField = ({ editing, field, value, size, ...props }) => {
const classes = fieldUseStyles() const classes = fieldUseStyles()
console.log('FIELDDDDDDDD', field)
const classNames = { const classNames = {
[classes.field]: true, [classes.field]: true,
[classes.notEditing]: !editing [classes.notEditing]: !editing
@ -174,7 +173,6 @@ const EditableCard = ({
{({ values, touched, errors, setFieldValue }) => ( {({ values, touched, errors, setFieldValue }) => (
<Form> <Form>
<PromptWhenDirty /> <PromptWhenDirty />
{console.log(values, touched, errors, 'FORMIK STATUS')}
<div className={classes.row}> <div className={classes.row}>
<Grid container> <Grid container>
<Grid container direction="column" item xs={6}> <Grid container direction="column" item xs={6}>

24
package-lock.json generated
View file

@ -11479,6 +11479,30 @@
"resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz", "resolved": "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz",
"integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==" "integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg=="
}, },
"graphql-upload": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-12.0.0.tgz",
"integrity": "sha512-ovZ3Q7sZ17Bmn8tYl22MfrpNR7nYM/DUszXWgkue7SFIlI9jtqszHAli8id8ZcnGBc9GF0gUTNSskYWW+5aNNQ==",
"requires": {
"busboy": "^0.3.1",
"fs-capacitor": "^6.2.0",
"http-errors": "^1.8.0",
"isobject": "^4.0.0",
"object-path": "^0.11.5"
},
"dependencies": {
"fs-capacitor": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-6.2.0.tgz",
"integrity": "sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw=="
},
"isobject": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
"integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA=="
}
}
},
"graphql-ws": { "graphql-ws": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.1.0.tgz", "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.1.0.tgz",

View file

@ -41,6 +41,7 @@
"graphql-iso-date": "^3.6.1", "graphql-iso-date": "^3.6.1",
"graphql-tools": "^7.0.2", "graphql-tools": "^7.0.2",
"graphql-type-json": "^0.3.1", "graphql-type-json": "^0.3.1",
"graphql-upload": "12.0.0",
"helmet": "^3.8.1", "helmet": "^3.8.1",
"inquirer": "^5.2.0", "inquirer": "^5.2.0",
"json2csv": "^5.0.3", "json2csv": "^5.0.3",