UNPKG

ea-crm-service-connector

Version:

A connector for Microsoft Dynamics CRM instances

234 lines (217 loc) 8.07 kB
'use strict' const Wreck = require('wreck') const oauth = require('./oauth') const configHelper = require('../../helpers/config-helper') const crmConfig = configHelper.getCrmConfig() const wreck = Wreck.defaults({ headers: {'Content-Type': 'application/json'}, events: true }) const wreckReadOptions = { json: true } if (process.env.CRM_API_DEBUG === 'all' || process.env.CRM_API_DEBUG === 'error') { wreck.events.on('response', async (err, details) => { try { const uri = details.uri const status = details.res ? details.res.statusCode : 400 const statusFamily = status / 100 const errorResponse = statusFamily === 4 || statusFamily === 5 if (process.env.CRM_API_DEBUG === 'all' || (process.env.CRM_API_DEBUG === 'error' && errorResponse)) { const time = Date.now() - details.start console.log(`CRM ${uri.method} request completed in (${time}ms)`) console[errorResponse ? 'error' : 'log'](`[${status}] ${decodeURI(uri.href)}`) } } catch (e) { console.error(`Error logging wreck request`, e) } }) } function decorateError (err, errorPayload = null) { // For backwards compatibility with previous versions of the crm-service-connector, which didn't return an Error object err.status = !!err.status err.error = errorPayload || err return err } const self = module.exports = { checkConnection: function (callback) { if (self.connected === true) { crmConfig.dynamics.connectionConfig.headers.Authorization = oauth.getActiveToken().tokenType + ' ' + oauth.getActiveToken().accessToken callback(null, true) } else { self.init(function (err, result) { if (err) { callback(decorateError(err)) } else { crmConfig.dynamics.connectionConfig.headers.Authorization = oauth.getActiveToken().tokenType + ' ' + oauth.getActiveToken().accessToken self.connected = true callback(null, true) } }) } }, connected: false, request: function (params) { params.time = process.hrtime() self.checkConnection(function (errCon, conResult) { if (errCon) return params.callback(decorateError(errCon)) params.options.headers.Authorization = crmConfig.dynamics.connectionConfig.headers.Authorization // Create the error object outside of the request callback for a more useful stacktrace const defaultResponseError = new Error(`Unexpected response from CRM endpoint ${params.uri}`) const crmResponseHandler = async function (err, result) { params.retryCounter++ let statusCode = result && result.statusCode ? parseInt(result.statusCode) : 400 if (err) { // console.log(`Error returned by CRM for URL ${params.options.baseUrl} with full URI ${params.uri}`, err) if (err.output && err.output.statusCode) { statusCode = err.output.statusCode } } if (result) { const responseJson = {headers: result.headers, statusCode: statusCode, payload: null} responseJson.headers.returnedId = getOdataIdFromHeader(result.headers) try { const payload = await Wreck.read(result, wreckReadOptions) if (payload && payload.error) { // console.log('not found data 404: ', params.options.baseUrl, params.uri) err = err || defaultResponseError return params.callback(decorateError(err, payload.error)) } else { responseJson.payload = payload return params.callback(null, responseJson) } } catch (responseReadError) { return params.callback(decorateError(responseReadError)) } } else if ((err || statusCode > 399) && params.retryIfFailed && params.retryCounter < crmConfig.dynamics.endpointRetries) { setTimeout(() => { self.request(params) }, params.retryWaitMillisecs) } else { return params.callback(decorateError(err || defaultResponseError)) } } ;(async () => { let result, error try { result = await wreck.request(params.method, params.uri, params.options) } catch (err) { error = err } finally { await crmResponseHandler(error, result) } })() }) }, get: function (uri, retryIfFailed, callback) { const params = _getParams(retryIfFailed) params.method = 'GET' params.uri = cleanString(uri) params.callback = callback self.request(params) }, post: function (payload, uri, retryIfFailed, callback) { let params = _getParams(retryIfFailed) params.method = 'POST' params = self.checkIfPostOnBehalfIsRequired(payload, params) params.options.payload = JSON.stringify(payload) params.callback = callback params.uri = cleanString(uri) self.request(params) }, put: function (payload, uri, retryIfFailed, callback) { let params = _getParams(retryIfFailed) params.method = 'PUT' params = self.checkIfPostOnBehalfIsRequired(payload, params) params.options.payload = JSON.stringify(payload) params.callback = callback params.uri = cleanString(uri) self.request(params) }, patch: function (payload, uri, retryIfFailed, callback) { let params = _getParams(retryIfFailed) params.method = 'PATCH' params = self.checkIfPostOnBehalfIsRequired(payload, params) params.options.payload = JSON.stringify(payload) params.callback = callback params.uri = cleanString(uri) self.request(params) }, delete: function (uri, retryIfFailed, callback) { const params = _getParams(retryIfFailed) params.method = 'DELETE' params.callback = callback params.uri = uri self.request(params) }, init: function (callback) { oauth.init(callback) }, authenticate: function (username, password, callback) { oauth.authenticate(username, password, function (err, activeToken) { if (err) return callback(err) callback(null, activeToken) }) }, checkIfPostOnBehalfIsRequired: function (payload, params) { try { if (payload && payload.actAsUser && payload.actAsUser.userId) { params.options.headers.MSCRMCallerID = payload.actAsUser.userId try { delete payload.actAsUser } catch (err) {} } } catch (error) { return params } return params } } function getOdataIdFromHeader (header) { let id = null if (header['odata-entityid']) { try { const strId = header['odata-entityid'].toString() id = strId.substring(strId.indexOf('(') + 1, strId.indexOf(')')) } catch (error) { id = null } } return id } // Default params function _getParams (retryIfFailed) { const params = { method: 'GET', // GET, POST, PUT, DELETE uri: '/', retryCounter: 0, oAuthToken: true, headers: {}, callback: null, retryIfFailed: retryIfFailed, endpointTimeOut: crmConfig.dynamics.endpointTimeOut, endpointRetries: crmConfig.dynamics.endpointRetries, retryWaitMillisecs: crmConfig.dynamics.endpointRetryWaitMillisecs, options: { baseUrl: crmConfig.dynamics.endpointUrl, payload: null, headers: crmConfig.dynamics.connectionConfig.headers, timeout: crmConfig.dynamics.endpointTimeOut, // 30 second, default: unlimited maxBytes: 99048576 // 1 MB, default: unlimited } } return JSON.parse(JSON.stringify(params)) } function cleanString (str) { let newStr = '' newStr = str.replace(/\s{2,10}/g, ' ') newStr = newStr.replace(/[\n\r]+/g, '') newStr = newStr.replace(/(\r\n|\n|\r)/gm, '') newStr = newStr.replace(/[^\x20-\x7E]/gmi, '') newStr = newStr.replace(/[\n\r]+/g, '') newStr = newStr.replace(/[\f]+/g, '') newStr = newStr.replace(/[\b]+/g, '') newStr = newStr.replace(/[\t]+/g, '') newStr = newStr.replace(/[\v]+/g, '') newStr = newStr.replace(/'/g, '%27%27') return newStr }