node-red-contrib-salesforce
Version:
A set of Node-RED nodes to interact with Salesforce and Force.com.
149 lines (138 loc) • 4.47 kB
JavaScript
const actionHelper = require('./lib/action_helper');
//array to back-up the node & disconnect the streaming client when redeploying flow
const clients = {};
/**
* Configure and/or reset a subscription node
* @param {NodeRED node} node - the Subscription Node
* @param {Node configuration object} config the stored configuration
*/
const setupSubscriptionNode = (node, config) => {
node.config = config;
node.subscriptionActive = false;
node.client = {}; // The faye client
// back-up the node and disconnect/clean-up when
// redeploying flow to avoid duplicated subscription
if (!clients[node.id]) {
clients[node.id] = node;
node.startSubscriptionId = config.replayId;
} else {
// On redeployment we want to start
node.startSubscriptionId = clients[node.id].lastReceivedId || config.replayId;
if (clients[node.id].client.disconnect) {
clients[node.id].client.disconnect();
clients[node.id] = node;
}
}
};
const handleStreamError = (node, err) => {
node.log('Subscription error!!!');
actionHelper.error(node, err.message, err);
node.client.disconnect();
node.subscriptionActive = false;
return node.error(err, err.message);
};
const handleStreamData = (node, data) => {
const lastReplayId = node.lastReplayId || node.startSubscriptionId;
const newReplayId = data.event.replayId;
if (lastReplayId === newReplayId) {
// TODO: should a duplicate replay Error be swallowed?
const err = new Error('Duplicate replay id');
actionHelper.error(node, newReplayId, err);
return;
}
actionHelper.receiving(node);
node.send({
payload: data
});
actionHelper.subscribed(node, node.subscriptionMessage);
};
const createSubscription = (subscribeOptions) => {
const node = subscribeOptions.node;
if (node.subscriptionActive) {
if (node.config.resubScribeOnDoubeSubscription) {
// Terminate subscription first, ignore the resolve/reject
terminateSubscription(node, () => {}, () => {});
} else {
// We don't subscribe again
subscribeOptions.resolve();
return;
}
}
const config = node.config;
const msg = subscribeOptions.msg;
const streamOpts = {
// Topic in message takes priority over configuration
topic: msg.topic || config.pushTopic,
replayId: msg.replayId || node.startSubscriptionId,
oauth: subscribeOptions.oauth
};
try {
const org = subscribeOptions.org;
node.subscriptionMessage = 'Subscribed to:' + streamOpts.topic;
const fayeOptions = {
oauth: subscribeOptions.oauth
//timeout: 90,
//retry: 60
};
node.client = org.createStreamClient(fayeOptions);
const stream = node.client.subscribe(streamOpts);
node.log(node.subscriptionMessage);
node.subscriptionActive = true;
stream.on('error', (err) => handleStreamError(node, err));
stream.on('data', (data) => handleStreamData(node, data));
} catch (ex) {
subscribeOptions.reject(ex);
}
actionHelper.subscribed(node, node.subscriptionMessage);
subscribeOptions.resolve();
};
const terminateSubscription = (subscribeOptions) => {
const node = subscribeOptions.node;
if (node.subscriptionActive) {
try {
if (node.client.disconnect) {
node.client.disconnect();
}
} catch (err) {
subscribeOptions.reject(err);
}
}
// Terminate in any case
node.subscriptionActive = false;
actionHelper.idle(node);
subscribeOptions.resolve();
};
const handleInput = (node, msg) => {
const action = msg.action || (node.subscriptionActive ? 'unsubscribe' : 'subscribe');
const realAction = (org, payload, nforce) => {
return new Promise((resolve, reject) => {
const subscribeOptions = {
node,
org,
msg,
oauth: payload.oauth,
nforce,
resolve,
reject
};
if (action === 'subscribe') {
createSubscription(subscribeOptions);
} else {
terminateSubscription(subscribeOptions);
}
});
};
actionHelper.inputToSFAction(node, msg, realAction);
};
// Make available to NodeRED
module.exports = function(RED) {
function Streaming(config) {
const node = this;
RED.nodes.createNode(node, config);
node.connection = RED.nodes.getNode(config.connection);
setupSubscriptionNode(node, config);
node.on('input', (msg) => handleInput(node, msg));
actionHelper.idle(node);
}
RED.nodes.registerType('streaming', Streaming);
};