pvserverhelper
Version:
This is an npm module design to perform common uses with pVelocity's pvserver
1,661 lines (1,489 loc) • 60.5 kB
JavaScript
/*global PV, emit*/
'use strict';
/* jshint strict: true */
/* jshint node: true */
/* jshint unused: false */
const iconv = require('iconv-lite');
const fs = require('fs');
const mongodb = require('mongodb');
const pvserver = require('pvserver');
const Converter = require('csvtojson').Converter;
const path = require('path');
const http = require('http');
const https = require('https');
const util = require('util');
require('pvjs');
module.exports = {
sessionJsapiCache: {},
callbackTimeouts: {},
addOrGetSessionJsapiObject: function(jsapi, sessionId) {
if (this.sessionJsapiCache.hasOwnProperty(sessionId)) {
return this.sessionJsapiCache[sessionId];
} else {
jsapi.isCached = true;
this.sessionJsapiCache[sessionId] = jsapi;
return jsapi;
}
},
getSessionJsapiObject: function(jsapi, sessionId) {
if (PV.isObject(this.sessionJsapiCache[sessionId])) {
return this.sessionJsapiCache[sessionId];
} else {
return jsapi;
}
},
removeSessionJsapiObject: function(jsapi, sessionId) {
if (PV.isObject(this.sessionJsapiCache[sessionId])) {
let cachedJsapi = this.sessionJsapiCache[sessionId];
cachedJsapi.isCached = null;
delete this.sessionJsapiCache[sessionId];
return cachedJsapi;
} else {
return jsapi;
}
},
cleanupForNonCached: async function(jsapi) {
if (PV.isBoolean(jsapi.isCached) && jsapi.isCached) {
} else {
await this.cleanup(jsapi);
}
},
serializePromises: async function(workFunction, workContext, workArray) {
let results = [];
let func = workFunction;
let context = workContext;
for (let i = 0; i < workArray.length; i++) {
if (PV.isArray(workFunction)) {
func = workFunction[i];
}
if (PV.isArray(workContext)) {
context = workContext[i];
}
results.push(await func.apply(context, workArray[i]));
}
return results;
},
cleanup: async function(jsapi) {
if (jsapi.mongoConn) {
try {
await jsapi.mongoConn.close();
} catch (ignore) {}
jsapi.mongoConn = null;
jsapi.mongoConnDb = null;
}
if (jsapi.sfdcConn) {
if (PV.isObject(jsapi.sfdc) === false || jsapi.sfdc.isSession !== true) {
try {
jsapi.sfdcConn.logoutAsync = util.promisify(jsapi.sfdcConn.logout);
await jsapi.sfdcConn.logoutAsync();
} catch (ignore) {}
}
jsapi.sfdcConn = null;
}
if (jsapi.pv) {
try {
await jsapi.pv.logout();
} catch (ignore) {}
jsapi.pv = null;
}
},
returnImmediately: function(callback) {
throw {
'code': 'RETURN_IMMEDIATELY',
'callback': function() {
callback(null, null);
}
};
},
scriptErrHandler: function(jsapi, callback, passError) {
let fn = async function(error) {
await this.cleanupForNonCached(jsapi);
let err = error;
if (error.json) {
err = this.getPVStatus(error.json)
}
err.message = this.getErrorMessage(err);
if (PV.isString(err.SCRIPT_ERROR_CODE)) {
err.code = err.SCRIPT_ERROR_CODE;
} else if (PV.isString(err.code)) {
} else if (PV.isString(err.Code)) {
err.code = err.Code;
} else if (PV.isFunction(err.code)) {
err.code = err.code();
} else {
err.code = 'JSAPI2_UNKNOWN_ERROR';
}
if (err.code === 'RETURN_IMMEDIATELY' && typeof(err.callback) === 'function') {
err.callback();
} else {
if (PV.isBoolean(passError) && passError) {
callback(null, {
'code': `${err.code}`,
'message': `${err.message}`
});
} else {
callback({
'code': `${err.code}`,
'message': `${err.message}`
}, null);
}
}
}.bind(this);
return fn;
},
genericErrHandler: function(jsapi, callback, passError) {
let fn = async function(err) {
await this.cleanupForNonCached(jsapi);
if (PV.isBoolean(passError) && passError) {
callback(null, {
'code': 'JSAPI2_UNKNOWN_ERROR',
'message': `The function encountered an unknown error.\n${JSON.stringify(err)}\n`
});
} else {
callback({
'code': 'JSAPI2_UNKNOWN_ERROR',
'message': `The function encountered an unknown error.\n${JSON.stringify(err)}\n`
}, null);
}
}.bind(this);
return fn;
},
getErrorMessage: function(error, includeTimestamp) {
let err = error;
if (error.json) {
err = this.getPVStatus(error.json);
}
let message = '';
if (PV.isString(err.SCRIPT_ERROR_MSG)) {
message = err.SCRIPT_ERROR_MSG;
} else if (PV.isString(err.message)) {
message = err.message;
} else if (PV.isString(err.Message)) {
message = err.Message;
} else if (PV.isFunction(err.message)) {
message = err.message();
} else if (PV.isString(err)) {
message = err;
} else {
message = 'No Relevant Message';
}
let timedMsg = '';
if (includeTimestamp === true) {
timedMsg = PV.getTimestamp() + ' - ' + message;
} else {
timedMsg = message;
}
return timedMsg;
},
setupLogger: function(jsapi, timestamp) {
if (PV.isObject(jsapi.logger) === false) {
jsapi.logger = {
timestamp: timestamp
};
}
let getTimedMsg = function(message) {
let timedMsg = '';
if (jsapi.logger.timestamp === true) {
timedMsg = PV.getTimestamp() + ' - ' + message;
} else {
timedMsg = message;
}
return timedMsg;
};
let logFunc = function(level, message) {
if (PV.isObject(jsapi.logger) && PV.isFunction(jsapi.logger.log)) {
jsapi.logger.log(level, message);
} else if (PV.isFunction(jsapi.log)) {
jsapi.log(level, message);
} else if (PV.isFunction(jsapi[level])) {
jsapi[level](message);
} else {
console.log(`${level.toUpperCase()}: ` + message);
}
};
jsapi.logger.info = function(message) {
let timedMsg = getTimedMsg(message);
logFunc('info', timedMsg);
}.bind(this);
jsapi.logger.infoAsync = util.promisify(jsapi.logger.info);
jsapi.logger.warn = function(message) {
let timedMsg = getTimedMsg(message);
logFunc('warn', timedMsg);
}.bind(this);
jsapi.logger.warnAsync = util.promisify(jsapi.logger.warn);
jsapi.logger.error = function(error, throwError) {
let message = this.getErrorMessage(error, jsapi.logger.timestamp);
logFunc('error', message);
if (throwError !== false) {
throw error;
}
}.bind(this);
jsapi.logger.errorAsync = util.promisify(jsapi.logger.error);
jsapi.logger.startTime = function(message) {
let timerObj = {
startTime: new Date(),
message: message
};
return timerObj;
};
jsapi.logger.endTime = function(timerObj) {
let timedMsg = timerObj.message;
let endTime = new Date();
let elapsedTime = (endTime - timerObj.startTime) / 1e3;
timedMsg = timedMsg + ' - ElaspedTime: ' + elapsedTime + 's';
if (PV.isObject(jsapi.logger) && PV.isFunction(jsapi.logger.log)) {
jsapi.logger.log('TIMER', timedMsg);
} else if (PV.isFunction(jsapi.log)) {
jsapi.log('error', timedMsg);
} else {
console.log('TIMER: ' + timedMsg);
}
};
},
logActivity: async function(jsapi, type, source, tag, message) {
let curDate = new Date();
let isoDate = curDate.toISOString();
let params = {
Type: PV.escapeXml(type),
Source: PV.escapeXml(source),
Tag: PV.escapeXml(tag),
StartTime: isoDate
};
if (PV.isObject(message)) {
message = JSON.stringify(message);
}
if (PV.isString(message)) {
params.LogMessage = PV.escapeXml(message);
}
return jsapi.pv.sendRequest('LogActivity', params);
},
convertFile: async function(source, target, options, decoding, encoding) {
return new Promise(function(resolve, reject) {
let rd = fs.createReadStream(source);
let wr = fs.createWriteStream(target);
if (PV.isObject(options) === false) {
options = {};
}
this.convertStream(rd, wr, resolve, reject, options, decoding, encoding);
}.bind(this));
},
convertStream: function(rd, wr, successCallback, failureCallback, options, decoding, encoding) {
let converter = null;
if (PV.isObject(options)) {
converter = new Converter(options);
}
rd.on('error', rejectCleanup);
wr.on('error', rejectCleanup);
function rejectCleanup(err) {
rd.destroy();
wr.end();
failureCallback(err);
}
wr.on('finish', successCallback);
if (PV.isString(decoding) && PV.isString(encoding)) {
if (PV.isObject(options)) {
rd.pipe(iconv.decodeStream(decoding))
.pipe(iconv.encodeStream(encoding))
.pipe(converter)
.pipe(wr);
} else {
rd.pipe(iconv.decodeStream(decoding))
.pipe(iconv.encodeStream(encoding))
.pipe(wr);
}
} else if (PV.isObject(options)) {
rd.pipe(converter).pipe(wr);
} else {
rd.pipe(wr);
}
},
saveFile: async function(url, dest, headers) {
return new Promise(function(resolve, reject) {
let file = fs.createWriteStream(dest);
let options = {
method: 'GET',
gzip: true,
headers: {
'user-agent': 'pvserverhelper',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9,fr;q=0.8,ro;q=0.7,ru;q=0.6,la;q=0.5,pt;q=0.4,de;q=0.3',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
}
};
if (PV.isObject(headers)) {
for (let headerKey in headers) {
options.headers[headerKey] = headers[headerKey];
}
}
let reqClient = http;
if (url.startsWith('https')) {
reqClient = https;
}
let req = reqClient.request(url, options, function(res) {
res.pipe(file);
res.on('end', () => {
resolve();
});
});
req.on('error', (e) => {
file.end();
reject(e);
});
});
},
execServlet: async function(jsapi, headers, operation, params) {
return new Promise(function(resolve, reject) {
let options = {
headers: {
'user-agent': 'pvserverhelper',
'content-type': 'application/x-www-form-urlencoded',
'cache-control': 'no-cache',
'pragma': 'no-cache'
}
};
if (PV.isObject(headers)) {
for (let headerKey in headers) {
options.headers[headerKey] = headers[headerKey];
}
}
let url = jsapi.pv.urlScheme + '://' + jsapi.pv.hostName + ':' + jsapi.pv.hostPort + '/admin/' + operation;
if (PV.isObject(params)) {
let args = [];
for (let paramKey in params) {
args.push(paramKey + '=' + params[paramKey]);
}
url = url + '?' + args.join('&');
}
options.method = 'POST';
let reqClient = http;
if (url.startsWith('https')) {
reqClient = https;
}
let req = reqClient.request(url, options, function(res) {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
res.body = data;
resolve(res);
});
});
req.on('error', (e) => {
reject(e);
});
if (PV.isNumber(options.timeout)) {
req.on('timeout', () => {
req.destroy();
reject(`${operation} timed out.`);
});
}
// Write data to request body
req.write('');
req.end();
});
},
exec: async function(jsapi, cmd, options) {
return new Promise(function(resolve, reject) {
try {
let cp = require('child_process');
if (PV.isObject(options) === false) {
options = {};
}
cp.exec(cmd, options, function(error, stdout, stderr) {
if (error) {
reject(error);
} else {
jsapi.logger.info('stdout: ' + stdout, true);
jsapi.logger.info('stderr: ' + stderr, true);
resolve({
'stdout': stdout,
'stderr': stderr
});
}
});
} catch (err) {
reject(err);
}
});
},
spawn: async function(jsapi, cmd, args, options) {
return new Promise(function(resolve, reject) {
try {
let cp = require('child_process');
if (PV.isObject(options) === false) {
options = {};
}
cp.spawn(cmd, args, options, function(pid, output, stdout, stderr, status, signal, error) {
if (error) {
reject(error);
} else {
jsapi.logger.info('stdout: ' + stdout, true);
jsapi.logger.info('stderr: ' + stderr, true);
resolve({
'pid': pid,
'output': output,
'stdout': stdout,
'stderr': stderr,
'status': status,
'signal': signal
});
}
});
} catch (err) {
reject(err);
}
});
},
dropSomeCollections: async function(jsapi, matchFunction) {
return jsapi.mongoConnDb.listCollections({}).toArray().then(function(result) {
let promises = [];
result.forEach(function(collection) {
if (matchFunction(collection.name)) {
promises.push(jsapi.mongoConnDb.collection(collection.name).drop());
}
});
return Promise.all(promises);
});
},
dropCollection: async function(jsapi, collectionName) {
return jsapi.mongoConnDb.listCollections({
name: collectionName
}).toArray().then(function(result) {
if (result.length > 0) {
return jsapi.mongoConnDb.collection(collectionName).drop();
} else {
return;
}
});
},
createCollection: async function(jsapi, collectionName, drop, indices) {
return jsapi.mongoConnDb.listCollections({
name: collectionName
}).toArray().then(function(result) {
if (result.length === 0) {
return jsapi.mongoConnDb.createCollection(collectionName);
} else if (drop) {
return jsapi.mongoConnDb.collection(collectionName).drop().then(function() {
return jsapi.mongoConnDb.createCollection(collectionName);
});
} else if (PV.isArray(indices)) {
return jsapi.mongoConnDb.collection(collectionName).indexInformation().then(function(result) {
let names = ['_id_'];
indices.forEach(function(info) {
names.push(Object.keys(info.key).join('_1_') + '_1');
});
let promises = [];
for (let indexName in result) {
if (names.includes(indexName) === false) {
promises.push(jsapi.mongoConnDb.collection(collectionName).dropIndex(indexName));
}
}
return Promise.all(promises);
});
} else {
return jsapi.mongoConnDb.collection(collectionName).dropIndexes();
}
}).then(function(result) {
if (PV.isArray(indices)) {
return jsapi.mongoConnDb.collection(collectionName).createIndexes(indices);
} else {
return result;
}
}.bind(this));
},
// this replaces your source collection with a projection that includes the lookup fields
// lookupField: field in lookupCollectionName that is being looked up
// sourceKey: field in sourceCollectionName used as a key to match with lookupCollectionName
// lookupKey: fields in lookupCollectionName used $project to construct a key that matches sourceKey
// defaultValue: a $set value used to set a default for the lookupField
// rename: field that the lookup will be set to, defaulted to lookupField
// tag: field that will be tagged true if lookup applied
// let lookupInfo = {
// 'lookupField': {
// sourceKey: 'sourceKey',
// lookupKey: {
// $toUpper: {
// $concat: ['$lookupProperty1', '-', '$lookupProperty2']
// }
// },
// defaultValue: 'defaultValue',
// rename: 'rename',
// tag: 'tag'
// }
// };
aggregateLookup: async function(jsapi, sourceCollectionName, lookupCollectionName, lookupInfo, lookupOperations) {
let id = PV.getTimestamp() + Math.random();
let tempLookupCollection = 'AG_' + PV.createHash(lookupCollectionName + '_' + id, 32);
let tempSourceCollection = 'AG_' + PV.createHash(sourceCollectionName + '_' + id, 32);
let lookupDuplicate = [];
let indices1 = [];
let project = {
_id: 0
};
for (let lookupField in lookupInfo) {
project[lookupField] = '$' + lookupField;
let lookup = lookupInfo[lookupField];
let lookupKeyString = JSON.stringify(lookup.lookupKey);
let lookupKey = PV.createHash(lookupKeyString + '1_' + id, 32);
if (lookupDuplicate.indexOf(lookupKeyString) === -1) {
project[lookupKey] = PV.isString(lookup.lookupKey) ? '$' + lookup.lookupKey : lookup.lookupKey;
lookupDuplicate.push(lookupKeyString);
let key = {};
key[lookupKey] = 1;
indices1.push({
key: key
});
}
}
lookupDuplicate = [];
let promises = [];
promises.push(jsapi.mongoConnDb.collection(sourceCollectionName).indexInformation({ full: true }));
promises.push(this.createCollection(jsapi, tempLookupCollection, true, indices1));
return Promise.all(promises).then(function(results) {
let pipeline = [];
if (PV.isArray(lookupOperations)) {
lookupOperations.forEach(function(operation) {
pipeline.push(operation);
});
}
pipeline.push({
$project: project
});
pipeline.push({
$out: tempLookupCollection
});
let indices2 = results[0];
indices2.forEach(function(index) {
for (let prop in index) {
if (['name', 'unique', 'key'].includes(prop) === false) {
delete index[prop];
}
}
});
let promises = [];
promises.push(this.getAggregateProjectMapping(jsapi, sourceCollectionName));
promises.push(this.createCollection(jsapi, tempSourceCollection, true, indices2));
promises.push(jsapi.mongoConnDb.collection(lookupCollectionName).aggregate(pipeline, {
allowDiskUse: true
}).toArray());
return Promise.all(promises);
}.bind(this)).then(function(results) {
let project = results[0];
let pipelineLookup = [];
let pipelineUnwind = [];
let tempKeyDuplicate = [];
for (let lookupField in lookupInfo) {
let lookup = lookupInfo[lookupField];
let lookupKeyString = JSON.stringify(lookup.lookupKey);
let lookupKey = PV.createHash(lookupKeyString + '1_' + id, 32);
let tempKey = PV.createHash(lookup.sourceKey + '2_' + id, 32);
let lookupFieldKey = PV.createHash(lookupField + '3_' + id, 32);
if (lookupDuplicate.indexOf(lookupKeyString) === -1 && tempKeyDuplicate.indexOf(tempKey) === -1) {
pipelineLookup.push({
$lookup: {
from: tempLookupCollection,
localField: lookup.sourceKey,
foreignField: lookupKey,
as: tempKey
}
});
pipelineUnwind.push({
$unwind: {
path: '$' + tempKey,
preserveNullAndEmptyArrays: true
}
});
}
project[lookupFieldKey] = '$' + tempKey + '.' + lookupField;
if (lookupDuplicate.indexOf(lookupKeyString) === -1) {
lookupDuplicate.push(lookupKey);
}
if (tempKeyDuplicate.indexOf(tempKey) === -1) {
tempKeyDuplicate.push(tempKey);
}
}
let pipeline = pipelineLookup.concat(pipelineUnwind);
pipeline.push({
$project: project
});
pipeline.push({
$out: tempSourceCollection
});
return jsapi.mongoConnDb.collection(sourceCollectionName).aggregate(pipeline, {
allowDiskUse: true
}).toArray();
}.bind(this)).then(async function() {
let bulk = jsapi.mongoConnDb.collection(tempSourceCollection).initializeOrderedBulkOp();
for (let lookupField in lookupInfo) {
let lookup = lookupInfo[lookupField];
let lookupFieldKey = PV.createHash(lookupField + '3_' + id, 32);
let defaultValue = lookup.defaultValue;
if (PV.isNull(defaultValue) === false && PV.isUndefined(defaultValue) === false) {
let filter = {};
filter[lookupFieldKey] = {
$exists: false
};
let set = {};
set[PV.isString(lookup.rename) ? lookup.rename : lookupField] = defaultValue;
bulk.find(filter).update({
$set: set
});
}
let rename = {};
rename[lookupFieldKey] = PV.isString(lookup.rename) ? lookup.rename : lookupField;
let filter2 = {};
filter2[lookupFieldKey] = {
$exists: true
};
let update = {
$rename: rename
};
if (PV.isString(lookup.tag)) {
let set = {};
set[lookup.tag] = true;
update.$set = set;
}
bulk.find(filter2).update(update);
}
if (bulk.length > 0) {
await bulk.execute();
}
let promises = [];
promises.push(this.dropCollection(jsapi, tempLookupCollection));
promises.push(this.dropCollection(jsapi, sourceCollectionName));
return Promise.all(promises);
}.bind(this)).then(function() {
return jsapi.mongoConnDb.collection(tempSourceCollection).rename(sourceCollectionName);
}.bind(this));
},
createExpressionMapping: function(objectOrArray, accumulator, aggregated, include_id) {
let arr = null;
if (PV.isArray(objectOrArray)) {
arr = objectOrArray;
} else if (PV.isObject(objectOrArray)) {
arr = Object.keys(objectOrArray);
}
let project = {};
if (PV.isArray(arr)) {
arr.forEach(function(element) {
let value = null;
if (aggregated === true) {
value = '$_id.' + element;
} else {
value = '$' + element;
}
if (PV.isString(accumulator)) {
project[element] = {};
project[element][accumulator] = value;
} else {
project[element] = value;
}
});
}
if (include_id !== true) {
project._id = 0;
}
return project;
},
find: async function(jsapi, collectionName, id, projection) {
let filter = {};
if (PV.isString(id)) {
filter._id = new mongodb.ObjectId(id);
} else if (PV.isObject(id)) {
filter._id = id;
}
if (PV.isObject(projection) === false) {
projection = {};
}
return jsapi.mongoConnDb.collection(collectionName).find(filter).project(projection).toArray();
},
copy: async function(jsapi, sourceCollection, targetCollection, filter, projection, overwriteKey) {
if (PV.isObject(filter) === false) {
filter = {};
}
if (PV.isObject(projection) === false) {
projection = {};
}
let batchSize = 2000;
let bulk = jsapi.mongoConnDb.collection(targetCollection).initializeOrderedBulkOp();
let cursor = await jsapi.mongoConnDb.collection(sourceCollection).find(filter).project(projection);
while (await cursor.hasNext()) {
let item = await cursor.next();
if (bulk.length > batchSize) {
await bulk.execute();
bulk = jsapi.mongoConnDb.collection(targetCollection).initializeOrderedBulkOp();
}
if (PV.isString(overwriteKey)) {
let filter2 = {};
filter2[overwriteKey] = item[overwriteKey];
bulk.find(filter2).deleteOne();
}
bulk.insert(item);
}
if (bulk.length > 0) {
return await bulk.execute();
} else {
return null;
}
},
move: async function(jsapi, sourceCollection, targetCollection, filter, cleanId) {
if (PV.isObject(filter) === false) {
filter = {};
}
let batchSize = 2000;
let count = await jsapi.mongoConnDb.collection(sourceCollection).find(filter).count();
while (count > 0) {
let insertedIds = [];
let sourceDocs = await jsapi.mongoConnDb.collection(sourceCollection).find(filter).limit(batchSize).toArray();
let bulk = jsapi.mongoConnDb.collection(targetCollection).initializeUnorderedBulkOp();
sourceDocs.forEach(function(doc) {
insertedIds.push(doc._id);
if (cleanId === true) {
delete doc._id;
}
bulk.insert(doc);
});
if (bulk.length > 0) {
await bulk.execute();
}
await jsapi.mongoConnDb.collection(sourceCollection).deleteMany({ _id: { $in: insertedIds } });
count = await jsapi.mongoConnDb.collection(sourceCollection).find(filter).count();
}
if (PV.isEmptyObject(filter)) {
return await this.dropCollection(jsapi, sourceCollection);
} else {
return null;
}
},
getProperties: async function(jsapi, collectionName) {
return jsapi.mongoConnDb.collection(collectionName).aggregate([{
$project: {
arrayofkeyvalue: {
$objectToArray: '$$ROOT'
}
}
}, {
$unwind: '$arrayofkeyvalue'
}, {
$group: {
_id: null,
allkeys: {
$addToSet: '$arrayofkeyvalue.k'
}
}
}]).toArray().then(function(result) {
return result[0].allkeys;
});
},
getABOMProperties: async function(jsapi, appName, objectName) {
return jsapi.mongoConnDb.collection('ABOM_Apps').find({
Name: appName
}).project({
_id: 1
}).toArray().then(function(appArr) {
let appId = appArr[0]._id;
return jsapi.mongoConnDb.collection('ABOM_Objects').find({
Name: objectName,
App: appId
}).project({
_id: 1
}).toArray();
}).then(function(objArr) {
let objId = objArr[0]._id;
return jsapi.mongoConnDb.collection('ABOM_Properties').find({
Object: objId,
Type: {
$nin: ['expr', 'abomPropertyType', 'function']
}
}).project({
_id: 1,
Name: 1
}).toArray();
}).then(function(propArr) {
let properties = {};
propArr.forEach(function(propObj) {
properties[propObj.Name] = propObj._id;
});
return properties;
});
},
getAggregateProjectMapping: async function(jsapi, collectionName) {
return this.getProperties(jsapi, collectionName).then(function(result) {
return this.createExpressionMapping(result);
}.bind(this));
},
cleanupChildren: async function(jsapi, collectionName, id, childrenMap) {
let projection = {};
for (let child in childrenMap) {
projection[childrenMap[child]] = 1;
}
return this.find(jsapi, collectionName, id, projection).then(function(result) {
let promises = [];
let set = {};
for (let child in childrenMap) {
let targets = result[0][childrenMap[child]];
if (PV.isArray(targets)) {
set[childrenMap[child]] = [];
} else {
set[childrenMap[child]] = null;
}
let filter = null;
if (PV.isArray(targets)) {
filter = {
_id: {
$in: targets
}
};
} else if (PV.isObject(targets)) {
filter = {
_id: targets
};
}
if (PV.isObject(filter)) {
promises.push(jsapi.mongoConnDb.collection(child).deleteMany(filter));
}
}
let updateFilter = {};
if (PV.isString(id)) {
updateFilter._id = new mongodb.ObjectId(id);
} else if (PV.isObject(id)) {
updateFilter._id = id;
}
promises.push(jsapi.mongoConnDb.collection(collectionName).updateOne(updateFilter, {
$set: set
}));
return Promise.all(promises);
});
},
isEmptyValue: function(value) {
return PV.isNull(value) || PV.isUndefined(value) ||
value === '-N/A-' || value === '- N/A -' ||
(PV.isString(value) && value.trim().length === 0);
},
setCallbackTimeout: function(key, timeout, callback) {
if (PV.isString(key)) {
if (this.callbackTimeouts.hasOwnProperty(key)) {
clearTimeout(this.callbackTimeouts[key]);
}
this.callbackTimeouts[key] = setTimeout(function() {
clearTimeout(this.callbackTimeouts[key]);
delete this.callbackTimeouts[key];
callback(null, null);
}.bind(this), timeout);
} else {
setTimeout(function() {
if (key.callbackTracker !== true) {
key.callbackTracker = true;
callback(null, null);
}
}, timeout);
}
},
checkCallbackTimeout: function(key, callback) {
if (PV.isString(key)) {
if (this.callbackTimeouts.hasOwnProperty(key)) {
clearTimeout(this.callbackTimeouts[key]);
delete this.callbackTimeouts[key];
callback(null, null);
}
} else {
if (key.callbackTracker !== true) {
key.callbackTracker = true;
callback(null, null);
}
}
},
login: async function(jsapi, protocol, host, port, username, password, credKey, sessionContext, options) {
return this.loginWithUrl(jsapi, protocol + '://' + host + ':' + port, username, password, credKey, sessionContext, options);
},
loginWithUrl: async function(jsapi, url, username, password, credKey, sessionContext, options) {
jsapi.logger.info('Logging in ' + url);
jsapi.pv = new pvserver.PVServerAPI(url);
if (PV.isObject(options)) {
for (let prop in options) {
jsapi.pv[prop] = options[prop];
}
}
let params = {
User: username,
TimeOut: jsapi.pv.timeOut,
DeviceName: jsapi.pv.device
}
if (PV.isString(password)) {
params.Password = password;
}
if (PV.isString(credKey)) {
params.CredentialKey = credKey;
}
if (PV.isObject(sessionContext)) {
params.SessionContext = {
Property: []
};
for (let key in sessionContext) {
params.SessionContext.Property.push({
'_attrs': {
'key': key
},
Value: sessionContext[key]
});
}
}
return jsapi.pv.sendRequest('Login', params).then(function(resp) {
jsapi.pv.user = resp.PVResponse.PVStatus.User;
jsapi.pv.role = resp.PVResponse.PVStatus.UserGroup;
jsapi.pv.response = resp;
return resp;
}).catch(function(err) {
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json));
} else {
jsapi.logger.error(err);
}
}.bind(this));
},
loginWithSession: async function(jsapi, options) {
if (PV.isObject(jsapi.pv) === false) {
jsapi.pv = new pvserver.PVServerAPI(jsapi.PVSession.engineSessionInfo.url);
if (PV.isObject(options)) {
for (let prop in options) {
jsapi.pv[prop] = options[prop];
}
}
return jsapi.pv.login(null, null, jsapi.PVSession.engineSessionInfo.apiKey).then(function(resp) {
jsapi.pv.response = resp;
return resp;
}).catch(function(err) {
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json));
} else {
jsapi.logger.error(err);
}
}.bind(this));
} else {
return jsapi.pv.response;
}
},
parseProviderModelUrl: function(url) {
let info = {};
let re = /:[\/][\/]([^\/]+)[\/]([^\/?]+)[?\/]?.*/;
let m = null;
if ((m = re.exec(url)) !== null) {
let host = m[1].split('@');
if (host.length > 1) {
let auth = host[0].split(':');
info.username = auth[0];
info.password = auth[1];
info.host = host[1];
} else {
info.username = null;
info.password = null;
info.host = m[1];
}
info.dbname = m[2];
} else {
info.host = null;
info.dbname = null;
}
let reOptions = /[?](.*)/;
if ((m = reOptions.exec(url)) !== null) {
let allOptions = m[1];
let options = allOptions.split('&');
info.options = {};
options.forEach(function(opt) {
let opValues = opt.split('=');
info.options[opValues[0]] = opValues[1];
});
} else {
info.options = null;
}
return info;
},
getProviderModelInfo: function(jsapi, tag, params) {
let infoTag = null;
if (tag === 'MongoDB') {
infoTag = 'mongo';
} else if (tag === 'Salesforce') {
infoTag = 'sfdc';
}
if (PV.isObject(jsapi[infoTag]) === false) {
jsapi[infoTag] = {};
}
try {
let sessionInfo = jsapi.PVSession.engineSessionInfo;
let providerModelInfo = sessionInfo.providerModelsByTag[tag];
let connectionInfo = null;
if (PV.isArray(providerModelInfo)) {
let userId = (params.userId ? params.userId : sessionInfo.user);
let appName = params.appName;
let mongoDBHostName = params.mongoDBHostName;
let html5AppName = null;
let html5DataSetId = null;
if (PV.isObject(sessionInfo.html5loginContext)) {
html5AppName = sessionInfo.html5loginContext.html5AppName;
html5DataSetId = sessionInfo.html5loginContext.html5DataSetId;
}
let providerModelId = sessionInfo.providerModelId;
if (PV.isString(userId) && (
(PV.isString(appName) && PV.isString(mongoDBHostName)) ||
PV.isString(providerModelId) ||
(PV.isString(html5AppName) && PV.isString(html5DataSetId)))) {
for (let i = 0; i < providerModelInfo.length; i++) {
if (providerModelInfo[i].userId === userId && (
(providerModelInfo[i].appName === appName && providerModelInfo[i].mongoDBHostName === mongoDBHostName) ||
(providerModelInfo[i].modelId === providerModelId) ||
(providerModelInfo[i].appName === html5AppName && providerModelInfo[i].dataSetId === html5DataSetId))) {
connectionInfo = providerModelInfo[i];
}
}
}
} else if (PV.isObject(providerModelInfo)) {
connectionInfo = providerModelInfo;
}
if (PV.isObject(connectionInfo)) {
for (let prop in connectionInfo) {
jsapi[infoTag][prop] = connectionInfo[prop];
}
}
jsapi.logger.info(infoTag + ' Engine Session Info');
} catch (e1) {
try {
jsapi[infoTag].modelId = JSON.parse(params.OpRequest).PVRequest.Operation.Params.ProfitModel.text;
jsapi.logger.info(infoTag + ' OpRequest');
} catch (e2) {
try {
let domain = tag.toUpperCase();
let models = JSON.parse(params.ProviderModels);
jsapi[infoTag].modelId = models[domain];
jsapi.logger.info(infoTag + ' ProviderModels');
} catch (e2) {}
}
}
return PV.isString(jsapi[infoTag].modelId);
},
createSalesforceProviderModel: async function(jsapi, dataSetId, username, password) {
if (PV.isObject(jsapi.sfdc) === false) {
jsapi.sfdc = {};
}
if (PV.isString(jsapi.sfdc.modelId)) {
jsapi.logger.info('Provider model id already already exist');
return jsapi.sfdc.response;
} else {
let dataSetQuery = {
'Type': 'Salesforce',
'KeyValue': [{
'Key': 'username',
'Value': username
}, {
'Key': 'password',
'Value': password
}, {
'Key': 'dataSetId',
'Value': dataSetId
}]
};
jsapi.logger.info('Creating provider model with ' + username + ' with ' + dataSetId);
return jsapi.pv.sendRequest('CreateProviderModel', dataSetQuery).then(function(resp) {
jsapi.sfdc.response = resp;
let status = this.getPVStatus(resp);
jsapi.sfdc.modelId = status.ModelId;
return resp;
}.bind(this)).catch(function(err) {
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json));
} else {
jsapi.logger.error(err);
}
}.bind(this));
}
},
createSalesforceProviderModelWithSession: async function(jsapi, dataSetId, access_token, instance_url) {
if (PV.isObject(jsapi.sfdc) === false) {
jsapi.sfdc = {};
}
if (PV.isString(jsapi.sfdc.modelId)) {
jsapi.logger.info('Provider model id already already exist');
return jsapi.sfdc.response;
} else {
let dataSetQuery = {
'Type': 'Salesforce',
'KeyValue': [{
'Key': 'dataSetId',
'Value': dataSetId
}, {
'Key': 'access_token',
'Value': access_token
}, {
'Key': 'instance_url',
'Value': instance_url
}]
};
jsapi.logger.info('Creating provider model with access_token on ' + instance_url + ' with ' + dataSetId);
return jsapi.pv.sendRequest('CreateProviderModel', dataSetQuery).then(function(resp) {
jsapi.sfdc.response = resp;
let status = this.getPVStatus(resp);
jsapi.sfdc.modelId = status.ModelId;
return resp;
}.bind(this)).catch(function(err) {
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json));
} else {
jsapi.logger.error(err);
}
}.bind(this));
}
},
createMongoProviderModel: async function(jsapi, username, appName, dataSetId, options) {
if (PV.isObject(jsapi.mongo) === false) {
jsapi.mongo = {};
}
if (PV.isString(jsapi.mongo.url) && PV.isString(jsapi.mongo.host) && PV.isString(jsapi.mongo.dbname)) {
jsapi.logger.info('Provider model id already already exist');
return await this.getProviderModelUrl(jsapi, options);
} else {
let dataSetQuery = {
'Type': 'MongoDB',
'KeyValue': [{
'Key': 'userId',
'Value': username
}, {
'Key': 'appName',
'Value': appName
}, {
'Key': 'dataSetId',
'Value': dataSetId
}]
};
jsapi.logger.info('Creating provider model with ' + username + ' for ' + appName + ' accessing dataSetId ' + dataSetId);
return jsapi.pv.sendRequest('CreateProviderModel', dataSetQuery).then(async function(resp) {
let status = this.getPVStatus(resp);
jsapi.mongo.modelId = status.ModelId;
jsapi.logger.info('Getting provider model url with ' + status.ModelId);
return await this.getProviderModelUrl(jsapi, options);
}.bind(this)).catch(function(err) {
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json), false);
} else {
jsapi.logger.error(err, false);
}
let dataSetQuery = {
'Type': 'MongoDB',
'KeyValue': [{
'Key': 'userId',
'Value': username
}, {
'Key': 'appName',
'Value': appName
}, {
'Key': 'mongoDBHostName',
'Value': dataSetId
}]
};
jsapi.logger.info('Attempting with ' + username + ' for ' + appName + ' accessing mongoDBHostName ' + dataSetId);
return jsapi.pv.sendRequest('CreateProviderModel', dataSetQuery).then(async function(resp) {
let status = this.getPVStatus(resp);
jsapi.mongo.modelId = status.ModelId;
jsapi.logger.info('Getting provider model url with ' + status.ModelId);
return await this.getProviderModelUrl(jsapi, options);
}.bind(this)).catch(function(err) {
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json));
} else {
jsapi.logger.error(err);
}
}.bind(this));
}.bind(this));
}
},
getProviderModelUrl: async function(jsapi, options) {
if (PV.isObject(jsapi.mongo) === false) {
jsapi.mongo = {};
}
if (PV.isString(jsapi.mongo.url) && PV.isString(jsapi.mongo.host) && PV.isString(jsapi.mongo.dbname)) {
jsapi.logger.info('Mongo Host, Database and Url already exist');
return jsapi.mongo.url;
} else {
return jsapi.pv.sendRequest('GetProviderModelUrl', {
'ProfitModel': jsapi.mongo.modelId
}).then(function(resp) {
let status = this.getPVStatus(resp);
let info = this.parseProviderModelUrl(status.Url);
jsapi.mongo.host = info.host;
jsapi.mongo.dbname = info.dbname;
let optionsStr = PV.convertObjectToStr(options);
if (optionsStr !== '') {
if (status.Url.indexOf('?') === -1) {
optionsStr = '?' + optionsStr;
} else {
optionsStr = '&' + optionsStr;
}
}
jsapi.mongo.url = status.Url + optionsStr;
if (PV.isObject(info.options)) {
jsapi.mongo.options = info.options;
} else {
jsapi.mongo.options = null;
}
if (PV.isString(info.username)) {
jsapi.mongo.username = info.username;
} else {
jsapi.mongo.username = null;
}
if (PV.isString(info.password)) {
jsapi.mongo.password = info.password;
} else {
jsapi.mongo.password = null;
}
if (PV.isString(info.host) && PV.isString(info.dbname)) {
jsapi.logger.info('Mongo Host: ' + jsapi.mongo.host);
jsapi.logger.info('Mongo Database: ' + jsapi.mongo.dbname);
return jsapi.mongo.url;
} else {
throw {
message: 'Unable to extract Mongo host from data source url',
code: 'Parsing Error'
};
}
}.bind(this)).catch(function(err) {
jsapi.mongo.url = null;
jsapi.mongo.host = null;
jsapi.mongo.dbname = null;
jsapi.mongo.options = null;
jsapi.mongo.username = null;
jsapi.mongo.password = null;
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json));
} else {
jsapi.logger.error(err);
}
}.bind(this));
}
},
setupMongoDBUrl: async function(jsapi, serverHost, serverPort, serverUserId, serverPassword, serverAuthDatabase, database, options) {
if (PV.isObject(jsapi.mongo) === false) {
jsapi.mongo = {};
}
if (PV.isString(jsapi.mongo.url) && PV.isString(jsapi.mongo.host) && PV.isString(jsapi.mongo.dbname)) {
jsapi.logger.info('Mongo Host, Database and Url already exist');
return jsapi.mongo.url;
} else {
if (PV.isString(serverHost) && PV.isString(database)) {
jsapi.mongo.host = serverHost;
jsapi.mongo.dbname = database;
let arr = [];
arr.push('mongodb://');
if (PV.isString(serverUserId)) {
arr.push(encodeURIComponent(serverUserId));
jsapi.mongo.username = serverUserId;
if (PV.isString(serverPassword)) {
arr.push(':' + encodeURIComponent(serverPassword));
jsapi.mongo.password = serverPassword;
} else {
jsapi.mongo.password = null;
}
arr.push('@');
} else {
jsapi.mongo.username = null;
jsapi.mongo.password = null;
}
arr.push(serverHost);
if (PV.isString(serverPort)) {
arr.push(':' + serverPort);
}
arr.push('/' + database);
let optionsStr = null;
if (PV.isString(serverAuthDatabase)) {
let optionsObj = {};
if (PV.isObject(options)) {
for (let k in options) {
optionsObj[k] = options[k];
}
}
optionsObj.authSource = serverAuthDatabase;
jsapi.mongo.options = optionsObj;
optionsStr = PV.convertObjectToStr(optionsObj);
} else {
if (PV.isObject(options)) {
jsapi.mongo.options = options;
optionsStr = PV.convertObjectToStr(options);
} else {
jsapi.mongo.options = null;
}
}
if (optionsStr !== '') {
arr.push('?' + optionsStr);
}
jsapi.mongo.url = arr.join('');
return jsapi.mongo.url;
} else {
jsapi.mongo.url = null;
jsapi.mongo.host = null;
jsapi.mongo.dbname = null;
jsapi.mongo.options = null;
jsapi.mongo.username = null;
jsapi.mongo.password = null;
let err = {
message: 'Missing data source url parameters',
code: 'Bad Parameters'
};
jsapi.logger.error(err);
}
}
},
createMongoDB: async function(jsapi) {
try {
if (PV.isObject(jsapi.mongoConn) && PV.isObject(jsapi.mongoConnDb) === false) {
jsapi.mongoConnDb = jsapi.mongoConn.db(jsapi.mongo.dbname);
return jsapi.mongoConnDb;
} else if (PV.isObject(jsapi.mongoConn) === false && PV.isObject(jsapi.mongoConnDb) === false) {
if (PV.isObject(jsapi.mongo) && PV.isString(jsapi.mongo.url) && PV.isString(jsapi.mongo.dbname)) {
jsapi.mongoConn = await mongodb.MongoClient.connect(jsapi.mongo.url);
jsapi.mongoConnDb = jsapi.mongoConn.db(jsapi.mongo.dbname);
return jsapi.mongoConnDb;
} else {
let err = {
message: 'Missing data source url or database name',
code: 'No Connection'
};
throw err;
}
} else {
return jsapi.mongoConnDb;
}
} catch (err) {
jsapi.mongoConn = null;
jsapi.mongoConnDb = null;
if (PV.isObject(err.json)) {
jsapi.logger.error(this.getPVStatus(err.json));
} else {
jsapi.logger.error(err);
}
}
},
getEntityGroupsAndFields: function(entity, entityArray) {
let result = null;
entityArray.forEach(function(obj) {
if (obj.Name === entity) {
result = obj;
}
});
result.Groups = result.Groups.Group;
result.Fields = result.Fields.Field;
return result;
},
convertGroupOrFieldArrayForQueryParams: function(arr) {
let uniqueArr = arr.filter(function(elem, index, self) {
if (PV.isString(elem)) {
return index === self.indexOf(elem);
} else if (PV.isObject(elem) && PV.isString(elem.text) && PV.isString(elem.attrs)) {
return true;
} else {
return false;
}
});
let newArray = [];
uniqueArr.forEach(function(elem) {
if (PV.isString(elem)) {
newArray.push({
'_attrs': {
'name': 'Res1'
},
'_text': elem
});
} else {
newArray.push({
'_attrs': {
'name': elem.attrs
},
'_text': elem.text
});
}
}.bind(this));
return newArray;
},
removeEntityRelationshipGroupAndFields: function(meta, entity, relationshipPatterns, not) {
if (PV.isBoolean(not) === false) {
not = false;
}
relationshipPatterns.forEach(function(pattern) {
let relationPattern = entity + '_' + pattern;
let reg = null;
eval('reg=/' + relationPattern + '.+/i');
let groups = meta.Groups;
let i = 0;
do {
if (reg.test(groups[i])) {
if (not) {
i++;
} else {
groups.splice(i, 1);
}
} else {
if (not) {
groups.splice(i, 1);
} else {
i++;
}
}
} while (i < groups.length);
let fields = meta.Fields;
let j = 0;
do {
if (reg.test(fields[j])) {
if (not) {
j++;
} else {
fields.splice(j, 1);
}
} else {
if (not) {
fields.splice(j, 1);
} else {
j++;
}
}
} while (j < fields.length);
});
},
getGroupsOrFieldsFromQueryParams: function(groupsOrFieldsParams) {
let groupsOrFields = [];
if (PV.isObject(groupsOrFieldsParams)) {
let values = null;
if (groupsOrFieldsParams.hasOwnProperty('Group')) {
values = PV.ensureArray(groupsOrFieldsParams.Group);
} else if (groupsOrFieldsParams.hasOwnProperty('Field')) {
values = PV.ensureArray(groupsOrFieldsParams.Field);
}
if (PV.isArray(values) && values.length > 0) {
values.forEach(function(value) {
if (PV.isString(value)) {
groupsOrFields.push(value);
} else if (PV.isObject(value)) {
if (PV.isString(value._text)) {
groupsOrFields.push(value._text);
} else if (PV.isString(value.text)) {
groupsOrFields.push(value.text);
}
}
});
}
}
return groupsOrFields;
},
getGroupValueFromQueryParams: function(queryParams, objectName, groupName) {
let result = null;
let regexp = /^([^=]+)=[']([^']+)[']$/i;
try {
let dp = queryParams.AndFilter;
PV.ensureArray(dp.OrFilter).forEach(function(compTerm) {
let category = null;
if (PV.isObject(compTerm._attrs)) {
category = compTerm._attrs.category;
}
if ((!objectName || (!category || category === objectName))) {
PV.ensureArray(compTerm.AndFilter).forEach(function(andTerm) {
PV.ensureArray(andTerm.Filter).forEach(function(filterTerm) {
let term = filterTerm;