const log = require('fancy-log'); // eslint-disable-line no-unused-vars const ConnectionReceiver = require('../connection/sf-connection-receiver'); require('../Types'); const MetadataDescribeProcessor = require('./describeProcessor/MetadataDescribeProcessor'); const ToolingDescribeProcessor = require('./describeProcessor/ToolingDescribeProcessor'); const SoapDescribeProcessor = require('./describeProcessor/SoapDescribeProcessor'); /** * Node Red Node that can describe many different types of things within Salesforce. */ class SfUniversalDescribe extends ConnectionReceiver { /** Constructor */ constructor() { super(); /** Indicates the tooling type */ this.PROCESSOR_TYPE_TOOLING = 'tooling'; /** Indicates the soql type */ this.PROCESSOR_TYPE_METADATA = 'metadata'; /** Indicates the soql type */ this.PROCESSOR_TYPE_SOAP = 'soap'; //-- initialize component properties } /** * Initialize the node red node * @param {object} RED - Node Red framework * @param {object} config - configuration for module from the node red editor * @param {object} nodeRedNode - the node red instance */ initialize(RED, config, nodeRedNode) { super.initialize(RED, config, nodeRedNode); //-- capture Node Red info this.RED = RED; this.config = config; this.nodeRedNode = nodeRedNode; //-- capture information from the nodeRedNode this.name = config.name; this.api = config.api; //-- handle events on the nodeRedNode nodeRedNode.on('input', (msg) => { // msg.payload = node.query; nodeRedNode.send(msg); }); return this; } /** * Determines the processor to use * @param {string} api - (soql|tooling) * @returns {import('./describeProcessor/AbstractDescribeProcessor')} - processor */ determineDescribeProcessor(api){ if (api === this.PROCESSOR_TYPE_METADATA){ return new MetadataDescribeProcessor(this.RED, this.config, this.nodeRedNode); } else if( api === this.PROCESSOR_TYPE_SOAP){ return new SoapDescribeProcessor(this.RED, this.config, this.nodeRedNode); } else if( api === this.PROCESSOR_TYPE_TOOLING){ return new ToolingDescribeProcessor(this.RED, this.config, this.nodeRedNode); } else { this.nodeRedNode.error(`Unknown describe processor type:${api}`); return null; } } /** * Determines whether to describe all * @param {object} msg - the message to capture the information from * @return {boolean} - whether to describe all (true) or by object (false) */ determineDescribeAll(msg){ return this.config.describeAll; } /** * Determines the object to deescribe * @param {object} msg - the message to capture the information from * @return {string} - the object to describe */ determineObjectName(msg){ if (this.config.objectName) { return this.RED.util.evaluateNodeProperty(this.config.objectName, this.config.objectNameType, this.nodeRedNode, msg); } else { return null; } } /** * Determines the msg target path to store the results * @param {object} msg - the message to capture the information from * @return {string} - the target path */ determineTarget(msg){ return this.config.target; } /** * Handle when the connection is established * @param {import('jsforce').Connection} connection - connection that was opened */ handleNewConnection(connection){ super.handleNewConnection(connection); this.nodeRedNode.removeAllListeners('input'); this.nodeRedNode.on('input', (msg) => { const describeAll = this.determineDescribeAll(msg); const objectName = this.determineObjectName(msg); const target = this.determineTarget(msg); //-- @TODO: verify there is no issue with garbage collection //-- we want to support multiple queries - there is a possibility of more than one running at a time const processor = this.determineDescribeProcessor(this.api); processor.execute( describeAll, objectName, target, this.connectionEmitter.connection, msg ); }); } /** * Handle when the connection is lost * @param {import('jsforce').Connection} connection - connection that was lost */ handleConnectionLost(connection){ super.handleConnectionLost(connection); } } /** * Initialize node-red node module * @param {NodeRed} RED - Node Red framework instance */ function setupNodeRed(RED){ RED.nodes.registerType('sf-universal-describe', function(config){ RED.nodes.createNode(this, config); //-- capture information from the config this.name = config.name; this.info = new SfUniversalDescribe() .initialize(RED, config, this) .listenToConnection('sfconn'); }); } //-- because it seems we cannot export the class outright... setupNodeRed.infoClass = SfUniversalDescribe; module.exports = setupNodeRed;