UNPKG

qforce

Version:

Commands to help with salesforce development.

315 lines (314 loc) 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.yaml2xml = exports.setStepReferences = exports.prepJsonForCsv = exports.pollBulkStatus = exports.poll = exports.handleNullValues = exports.getQueryAll = exports.getProp = exports.getFiles = exports.getRelativePath = exports.getAbsolutePath = exports.filterQueryFields = exports.deleteFolderRecursive = void 0; const path = require('path'); const fs = require('fs'); const sfdx = require('sfdx-node'); const csvjson = require('csvjson'); function deleteFolderRecursive(pathString) { let dataPath = pathString.split('/'); let basePath = path.join(process.cwd(), ...dataPath); if (fs.existsSync(basePath)) { fs.readdirSync(basePath).forEach((file, index) => { let curPath = path.join(process.cwd(), ...dataPath, file); if (fs.lstatSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(dataPath.join('/') + '/' + file); } else { // delete file fs.unlinkSync(curPath); } }); fs.rmdirSync(basePath); } } exports.deleteFolderRecursive = deleteFolderRecursive; function filterQueryFields(queryString, targetusername, externalIdField) { let filteredQuery = ''; try { let selectPart = queryString.substring(0, queryString.toLowerCase().indexOf('from')); let fromPart = queryString.substring(queryString.toLowerCase().indexOf('from')); let originalFields = selectPart.toLowerCase().split(/\s+/); originalFields.shift(); let sobjectName = fromPart.split(/\s+/)[1]; let jsonPath = getAbsolutePath('.qforce/definitions/' + targetusername + '/' + sobjectName.toLowerCase() + '.json'); let objectDefinition; if (fs.existsSync(jsonPath)) { objectDefinition = JSON.parse(fs.readFileSync(jsonPath)); } let createableFields = []; for (let field of objectDefinition.fields) { if (field.createable) createableFields.push(field.name.toLowerCase()); } for (let field of originalFields) { field = field.trim().replace(',', ''); if (!filteredQuery) { filteredQuery = 'SELECT ' + externalIdField + ' '; } else if (createableFields.includes(field)) { filteredQuery = filteredQuery + ', ' + field; } } filteredQuery = filteredQuery + ' ' + fromPart; } catch (err) { console.log(JSON.stringify(err, null, 2)); return queryString; } return filteredQuery; } exports.filterQueryFields = filterQueryFields; function getProp(object, prop) { if (object[prop]) return object[prop]; for (let key in object) { if (key.toLowerCase() === prop.toLowerCase()) { return object[key]; } } } exports.getProp = getProp; function getRelativePath(rawPath) { let relativePath = path.join(...rawPath.trim().split('/')); return relativePath; } exports.getRelativePath = getRelativePath; function getAbsolutePath(rawPath) { let relativePath = path.join(process.cwd(), ...rawPath.trim().split('/')); return relativePath; } exports.getAbsolutePath = getAbsolutePath; async function getFiles(dir) { const dirents = fs.readdirSync(dir, { withFileTypes: true }); const files = await Promise.all(dirents.map((dirent) => { const res = path.resolve(dir, dirent.name); return dirent.isDirectory() ? getFiles(res) : res; })); return Array.prototype.concat(...files); } exports.getFiles = getFiles; function getQueryFields(objectDefinition, filter) { let fieldNames = ''; let tooManyFields = objectDefinition.fields.length > 100; for (let field of objectDefinition.fields) { if (filter || tooManyFields) { if (!field.createable || field.type == 'reference' || (field.defaultedOnCreate && !field.updateable)) continue; } if (fieldNames) fieldNames = fieldNames + ', ' + field.name; else fieldNames = field.name; } return fieldNames; } function getQueryAll(query, targetusername, filter) { function buildQuery(objectDefinition) { let fieldNames = ''; let tooManyFields = objectDefinition.fields.length > 100; for (let field of objectDefinition.fields) { if (filter || tooManyFields) { if (!field.createable || field.type == 'reference' || (field.defaultedOnCreate && !field.updateable)) continue; } if (fieldNames) fieldNames = fieldNames + ', ' + field.name; else fieldNames = field.name; } if (fieldNames) query = query.replace(/\*/g, fieldNames); } return new Promise((resolve, reject) => { let sobjecttype = query.substring(query.toLowerCase().lastIndexOf('from')).split(/\s+/)[1].trim(); //console.log('sobjecttype: ' + sobjecttype) let defPath = getAbsolutePath('.qforce/definitions/' + sobjecttype + '.json'); if (fs.existsSync(defPath)) { buildQuery(JSON.parse(fs.readFileSync(defPath))); resolve(query); } else { sfdx.schema.sobjectDescribe({ targetusername: targetusername, sobjecttype: sobjecttype }) .then((objectDefinition) => { if (objectDefinition && objectDefinition.fields) { buildQuery(objectDefinition); resolve(query); } else { console.log(objectDefinition); reject('Could not get fields for object definition. Got ' + objectDefinition); } }) .catch((error) => { reject('Could not get object definition.\n' + error); }); } }); } exports.getQueryAll = getQueryAll; function handleNullValues(line) { for (let key of Object.keys(line)) { if (line[key] == '\u001b[1mnull\u001b[22m') line[key] = ''; if (line[key] == 'null') line[key] = ''; if (line[key] == null) line[key] = ''; } return line; } exports.handleNullValues = handleNullValues; function poll(fn, timeout, interval, context) { let endTime = Number(new Date()) + (timeout || 2000); interval = interval || 100; var checkCondition = function (resolve, reject) { // If the condition is met, we're done! let result = fn(context); if (result.state == 'Completed') { resolve(result); } // If the condition isn't met but the timeout hasn't elapsed, go again else if (Number(new Date()) < endTime) { setTimeout(checkCondition, interval, resolve, reject); } // Didn't match and too much time, reject! else { reject(new Error('timed out for ' + fn + ': ' + arguments)); } }; return new Promise(checkCondition); } exports.poll = poll; function pollBulkStatus(options, retries = 3, interval = 5000) { let endTime = Number(new Date()) + retries * interval; let statusResults; async function checkResults(resolve, reject) { statusResults = await sfdx.data.bulkStatus(options); if (statusResults && statusResults[0].state == 'Completed') { resolve(statusResults[0]); } // If the condition isn't met but the timeout hasn't elapsed, go again else if (Number(new Date()) < endTime) { console.log(JSON.stringify(statusResults[0], null, 4)); setTimeout(checkResults, interval, resolve, reject); } // Didn't match and too much time, reject! else { reject(new Error('Timed out:\n' + JSON.stringify(statusResults, null, 4))); } } ; return new Promise(checkResults); } exports.pollBulkStatus = pollBulkStatus; function prepJsonForCsv(line) { if (line.attributes) delete line.attributes; if (line.height) delete line.height; for (let key of Object.keys(line)) { //console.log(key + ': ' + line[key]) if (line[key] == '\u001b[1mnull\u001b[22m') delete line[key]; if (line[key] === null) delete line[key]; if (line[key] == 'null') delete line[key]; if (line[key] === "") delete line[key]; if (typeof line[key] === 'string') { line[key] = line[key].replace(/"/g, '""'); } else if (line[key] && Object.keys(line[key])) { prepJsonForCsv(line[key]); } } return line; } exports.prepJsonForCsv = prepJsonForCsv; function setStepReferences(step, basePath) { for (let reference of step.references) { let referencePath = getAbsolutePath(basePath + '/reference/' + reference + '.json'); step[reference] = JSON.parse(fs.readFileSync(referencePath, { encoding: 'utf8' })); } return step; } exports.setStepReferences = setStepReferences; function yaml2xml(featureYAML, xmlVersion) { let featureXML; featureXML = { declaration: { attributes: { version: '1.0', encoding: 'UTF-8' } }, elements: [ { type: 'element', name: 'Package', attributes: { xmlns: 'http://soap.sforce.com/2006/04/metadata' }, elements: [] } ] }; for (let metadataType in featureYAML) { let typesElement; typesElement = { type: 'element', name: 'types', elements: [] }; if (metadataType == 'ManualSteps') continue; if (featureYAML[metadataType]) { for (let metadataName of featureYAML[metadataType]) { typesElement.elements.push({ type: 'element', name: 'members', elements: [ { type: 'text', text: metadataName } ] }); } typesElement.elements.push({ type: 'element', name: 'name', elements: [ { type: 'text', text: metadataType } ] }); } featureXML.elements[0].elements.push(typesElement); } featureXML.elements[0].elements.push({ type: 'element', name: 'version', elements: [ { type: 'text', text: xmlVersion } ] }); return featureXML; } exports.yaml2xml = yaml2xml;