From 70f0fc992d57ada910ff5f32807b47ba75ba570d Mon Sep 17 00:00:00 2001 From: adriano Date: Fri, 4 Oct 2024 14:59:10 -0300 Subject: [PATCH] chore: add suport to xml request and response --- backend/app.js | 16 ++- backend/controllers/crmController.js | 156 ++++++++++++++++++++++++++- backend/package-lock.json | 64 +++++++++++ backend/package.json | 1 + backend/routes/crmRoute.js | 3 +- 5 files changed, 235 insertions(+), 5 deletions(-) diff --git a/backend/app.js b/backend/app.js index 32549ff..4210e4b 100644 --- a/backend/app.js +++ b/backend/app.js @@ -10,10 +10,24 @@ const express = require('express') const app = express() const session = require('express-session') +const bodyParser = require('body-parser'); // Para JSON +require('body-parser-xml')(bodyParser); // Para XML + // rest of the packages const morgan = require('morgan') // const fileUpload = require('express-fileupload') +// Middleware para lidar com JSON +app.use(bodyParser.json()); + +// Middleware para lidar com XML +app.use(bodyParser.xml({ + limit: '1MB', + xmlParseOptions: { + explicitArray: false, + } +})); + const rateLimiter = require('express-rate-limit') // Swagger @@ -51,7 +65,7 @@ app.use(cors()) app.use(xss()) app.use(morgan('tiny')) -app.use(express.json()) +// app.use(express.json()) // app.use(fileUpload()) diff --git a/backend/controllers/crmController.js b/backend/controllers/crmController.js index f736207..9822921 100644 --- a/backend/controllers/crmController.js +++ b/backend/controllers/crmController.js @@ -18,6 +18,9 @@ const { exchangeForTokens } = oauth2 const Company = require('../models/Company') const CRM = require('../models/CRM') const CustomError = require('../errors') +const CRM_Contact = require('../models/CRM_Contact') +const axios = require('axios') +const { get, set } = require('../utils/redisClient') const contactCreate = async (req, res) => { @@ -289,8 +292,8 @@ const getCrms = async (req, res) => { const webhook = async (req, res) => { - console.log('========> webhook req.body: ', JSON.stringify(req.body, null, 6)) - + console.log('========> webhook req.body: ', JSON.stringify(req.body, null, 6)) + console.log('========> req.body: ', req.body) if (!req.body?.meta) @@ -324,6 +327,73 @@ const webhook = async (req, res) => { res.status(StatusCodes.OK).send() } +const webhook_crm = async (req, res) => { + + console.log('========> webhook crm req.body: ', JSON.stringify(req.body, null, 6)) + + const responseXml = ` + + + + + true + + + ` + + const sObject = req.body['soapenv:Envelope']['soapenv:Body']?.notifications?.Notification?.sObject + + if (!sObject) { + res.set('Content-Type', 'text/xml') + return res.status(StatusCodes.OK).send(responseXml) + } + + const whoId = sObject['sf:WhoId'] + const EventSubtype = sObject['sf:EventSubtype'] + const StartDateTime = sObject['sf:StartDateTime'] + + + console.log('==========> crm EventSubtype: ', EventSubtype) + console.log('==========> crm StartDateTime: ', StartDateTime) + console.log('==========> crm StartDateTime timeStamp: ', timeStamp(StartDateTime)) + + + + if (EventSubtype == 'Event') { + + const contact = await CRM_Contact.findOne({ + companyId: "99", + crmBaseURL: 'nocompany-a9-dev-ed.develop.my.salesforce.com', + contactId: whoId + }) + + console.log('==========> crm whoId: ', whoId) + console.log('==========> crm contact: ', contact) + + const { phone } = contact + + const contactIdChatwoot = await getContactIdChatwoot(phone) + + if (!contactIdChatwoot) { + res.set('Content-Type', 'text/xml') + return res.status(StatusCodes.OK).send(responseXml) + } + + console.log('==========> crm contactIdChatwoot: ', contactIdChatwoot) + + const ticketId = await createConversation(contactIdChatwoot) + + console.log('==========> crm ticketId: ', ticketId) + + await toggleConversationStatus({ "status": "snoozed", "snoozed_until": timeStamp(StartDateTime) }, ticketId) + + } + + + res.set('Content-Type', 'text/xml') + return res.status(StatusCodes.OK).send(responseXml) +} + module.exports = { contactCreate, uploadCrmConfig, @@ -334,7 +404,87 @@ module.exports = { deleteCompany, testTemplate, getCrms, - webhook + webhook, + webhook_crm +} + + + + +async function getContactIdChatwoot(phone) { + + const config = { + method: 'get', + url: `http://172.31.187.47:3333/api/v1/accounts/15/contacts/search?q=${phone}`, + headers: { + 'api_access_token': 'WpeGuicvuQ3pyLvYQ11eAxxL' + } + } + + try { + const { data } = await axios(config) + + return data.payload[0].id + } catch (error) { + console.error(error) + } +} + +async function createConversation(contact_id) { + const data = JSON.stringify({ + "inbox_id": "2", + "contact_id": `${contact_id}`, + "status": "pending", + "team_id": "1" + }) + + const config = { + method: 'post', + url: 'http://172.31.187.47:3333/api/v1/accounts/15/conversations', + headers: { + 'api_access_token': 'WpeGuicvuQ3pyLvYQ11eAxxL', + 'Content-Type': 'application/json' + }, + data: data + } + + + try { + const { data } = await axios(config) + return data.id + } catch (error) { + console.error(error) + } +} + +async function toggleConversationStatus(payload, ticketId) { + + const config = { + method: 'post', + url: `http://172.31.187.47:3333/api/v1/accounts/15/conversations/${ticketId}/toggle_status`, + headers: { + 'api_access_token': 'WpeGuicvuQ3pyLvYQ11eAxxL', + 'Content-Type': 'application/json' + }, + data: JSON.stringify(payload) + } + + try { + const response = await axios(config) + console.log(JSON.stringify(response.data)) + } catch (error) { + console.error(error) + } +} + +function timeStamp(dateTimeISO8601) { + const date = new Date(dateTimeISO8601) + + date.setHours(date.getUTCHours() - 3) + + const timestamp = date.getTime() / 1000 + + return timestamp } diff --git a/backend/package-lock.json b/backend/package-lock.json index d50ad56..44ec232 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "axios": "^1.6.1", + "body-parser-xml": "^2.0.5", "cors": "^2.8.5", "dotenv": "^10.0.0", "express": "^4.17.1", @@ -301,6 +302,17 @@ "node": ">= 0.8" } }, + "node_modules/body-parser-xml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/body-parser-xml/-/body-parser-xml-2.0.5.tgz", + "integrity": "sha512-m1Kvr+0OVo1+t5hEgTrEQMIxFomck4682EJgFx4UpKcKVk9gViifgaFvSNwnQE+S10pPy8Q+dz9iWHYCol51Hw==", + "dependencies": { + "xml2js": "^0.5.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -2154,6 +2166,11 @@ "node": ">=6" } }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -2713,6 +2730,26 @@ "node": ">=8" } }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xss-clean": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/xss-clean/-/xss-clean-0.1.1.tgz", @@ -2981,6 +3018,14 @@ "type-is": "~1.6.17" } }, + "body-parser-xml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/body-parser-xml/-/body-parser-xml-2.0.5.tgz", + "integrity": "sha512-m1Kvr+0OVo1+t5hEgTrEQMIxFomck4682EJgFx4UpKcKVk9gViifgaFvSNwnQE+S10pPy8Q+dz9iWHYCol51Hw==", + "requires": { + "xml2js": "^0.5.0" + } + }, "boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -4377,6 +4422,11 @@ "sparse-bitfield": "^3.0.3" } }, + "sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -4801,6 +4851,20 @@ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, "xss-clean": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/xss-clean/-/xss-clean-0.1.1.tgz", diff --git a/backend/package.json b/backend/package.json index 2ce7c23..accc00f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "axios": "^1.6.1", + "body-parser-xml": "^2.0.5", "cors": "^2.8.5", "dotenv": "^10.0.0", "express": "^4.17.1", diff --git a/backend/routes/crmRoute.js b/backend/routes/crmRoute.js index d56cb92..c749f83 100644 --- a/backend/routes/crmRoute.js +++ b/backend/routes/crmRoute.js @@ -1,7 +1,7 @@ const express = require('express') const router = express.Router() const { authorization, } = require('../middleware/authentication') -const { contactCreate, testTemplate, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, getCrms, webhook } = require('../controllers/crmController') +const { contactCreate, testTemplate, webhook_crm, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, getCrms, webhook } = require('../controllers/crmController') const { fileUpload } = require("../utils") router.route('/create-contact').post(authorization, contactCreate) @@ -13,6 +13,7 @@ router.route('/oauth-callback').get(oauthCallBack) router.route('/install').get(install) router.route('/test').post(testTemplate) router.route('/webhook').post(webhook) +router.route('/webhook-crm').post(webhook_crm) router.route('/:companyId').get(authorization, getCrms)