Socket
Socket
Sign inDemoInstall

node-red-contrib-spark

Package Overview
Dependencies
114
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.1 to 2.0.0

images/parser-node-config.jpg

11

CHANGELOG.md

@@ -0,1 +1,8 @@

#### 2.0.0: Major Release
- Updated all node inline documentation and label formats
- api node now sends array responses as individual sequential messages
- parse node rebuilt to allow more than 1 property to be parsed from payload
- updated README.md to include updated documentation around parse and api node
#### 1.1.1: Maintenance Release

@@ -29,5 +36,5 @@

- Updated node labels to "api" and "webhook"
- Updated node category to "cisco_spark"
- updated node labels to "api" and "webhook"
- updated node category to "cisco_spark"
#### 1.0.0: Initial Release

2

package.json
{
"name": "node-red-contrib-spark",
"version": "1.1.1",
"version": "2.0.0",
"description": "Node-RED Nodes to integrate with the Cisco Spark API",

@@ -5,0 +5,0 @@ "dependencies": {

@@ -5,2 +5,4 @@ # node-red-contrib-spark

Version 2.0.0 [(changelog)](https://github.com/nmarus/node-red-contrib-spark/blob/master/CHANGELOG.md)
![](https://github.com/nmarus/node-red-contrib-spark/raw/master/images/flow01.jpg)

@@ -12,3 +14,3 @@

* **webhook** - This Node creates, removes, and manages the Webhook features of the Spark API. Once deployed, and subsequently triggered, the contents of the notification will be sent to the output of this Node.
* **parser** - This is a utility Node that accepts input from the output of either the Api or Webhook Node. This acts similarly to the `map` functionality provided in many programming languages and additionally provides options when dealing with input that is in the form of a collection (array of objects).
* **parser** - This is a utility Node that accepts input from the output of either the API or Webhook Node.
* **auth** - This is a Config Node that holds the credential Bearer Token for the Spark API. Once initially defined, either under the Api or Webhook Node, it will be available to select on all other Api and Webhook Nodes that are created. You can define multiple Auth profiles so as to work with different Spark Accounts or Bots within the same flow.

@@ -55,3 +57,3 @@

The Spark API Node sends REST queries via messages received by the input connector in the `msg.payload` object. Results of the API call are provided at the output in the `msg.payload` object.
The Spark API Node sends REST queries via messages received by the input connector in the `msg.payload` object. Results of the API call are provided at the output in the `msg.payload` object. If multiple records are returned from the Spark API Call, these are passed to the output as individual sequential messages. The `msg.parts` property is set appropriately for use with the `join` node if a single array payload is preferred.

@@ -66,3 +68,3 @@ ![](https://github.com/nmarus/node-red-contrib-spark/raw/master/images/api-node.jpg)

By convention the output from the Spark API call will have a `msg.payload` property containing the results of the API call in JSON format. The format of this JSON object will be the same as documented at [developer.ciscospark.com](https://developer.ciscospark.com) for the responses from the API call.
By convention, the output from the Spark API call will have a `msg.payload` property. This contains the results of the API call in JSON format. The format of this JSON object will be the same as documented at [developer.ciscospark.com](https://developer.ciscospark.com) for the responses from the API call.

@@ -72,9 +74,7 @@ Additionally the following are defined as part of the msg object:

* `msg.status` : http return code
* `msg.error` : error object (will evaluate to `null` when no error is present)
* `msg.error.message` : error message
* `msg.error.description` : error description (only available for certain errors)
* `msg.error.trackingId` : tracking id (only available for certain errors)
* `msg.headers` - response headers object
* `msg._msgid` - unique identifier
#### Multiple Results
If multiple records are returned from the Spark API Call, these are passed to the output as individual sequential messages. The `msg.parts` property is set appropriately for use with the `join` node if a single array payload is preferred.
**Example: Get Person by Email**

@@ -184,16 +184,4 @@

The Spark Parse Node allows parsing of messages received from either the Webhook or API Node. The parsed value is placed in the `msg.payload` of the first output. The value of the "parse" field is delivered in `msg.topic` for use with supporting functions like `join`. The original `msg.payload` is passed through to the second output.
The Spark Parse Node allows parsing of specific properties from the JSON `msg.payload` received from either the "Spark Webhook Node" or the "Spark API Node".
The output specifies how the `msg.payload` is formatted. Options are:
<ul>
<li>original - The original value of the property without modifying data
type.</li>
<li>object - The original value of the property placed into an object with
the object key being the parser value, or optionally the topic if
specified.</li>
</ul>
</p>
If the parser input receives an array, each element of the array is parsed individually. The results are returned as multiple sequential messages to the output with each msg having a `msg.payload` and `msg.topic` property.
![](https://github.com/nmarus/node-red-contrib-spark/raw/master/images/parser-node.jpg)

@@ -203,8 +191,6 @@

* **Parse** - The object property to parse from the input.
* **Output** - The selector on how to handle parsed output:
* **the individual property value** - Outputs the original value of the property without modifying data type.
* **a key/value object** - Outputs the original value of the property placed into an object with the object key being the parser value, or optionally the topic if specified.
* **Topic** - By default, the value of "parse" is used as msg.topic. This can be overridden here if needed.
Define each *"property"* to parse from the inbound `msg.payload`. Optionally, define a *"name"* to remap the property name used in the oubound JSON object. This allows remapping of properties such as `msg.payload.id` to `msg.payload.roomId`. If *"name"* is left undefined, it will use the same value as the original property. Add additional rows to define multiple properties and names to construct the output JSON object.
![](https://github.com/nmarus/node-red-contrib-spark/raw/master/images/parser-node-config.jpg)
## License

@@ -211,0 +197,0 @@

@@ -6,2 +6,3 @@ module.exports = function(RED) {

var fs = require('fs');
var _ = require('lodash');

@@ -44,7 +45,11 @@ var swaggerClient = null;

processResponse(msg, response);
}, function(error) {
processError(msg, error);
}, function(errMessage) {
if(errMessage) {
processError(new Error(errMessage));
} else {
processError(new Error('swagger client returned undefined error'));
}
});
} else {
processError(msg, 'error with spark api token or swagger client');
processError(new Error('spark api token or swagger client invalid or not defined'));
}

@@ -62,2 +67,3 @@ }

node.method = n.method;
node.id = n.id;

@@ -67,9 +73,2 @@ node.reqCount = 0;

// set default status
node.status({
fill: 'blue',
shape: 'ring',
text: 'Spark API: ready'
});
// set node status visual indicators

@@ -143,80 +142,53 @@ function setNodeStatus(state) {

}, 200);
};
}
function processResponse(msg, response) {
// extend msg object
msg.payload = {};
msg.error = {};
msg.status = 500;
reqPing();
// handle undefined response
if(typeof response === 'undefined') {
// set error message
msg.error.message = 'response not received';
var newMsg = {};
newMsg.topic = node.resource + '.' + node.method;
// show warning in debug window
node.warn(msg.error.message);
// handle object response
if(response && typeof response === 'object') {
// set node status
setNodeStatus('warning');
}
// handle string response
else if(typeof response === 'string') {
msg.payload = response;
// set error message
msg.error.message = 'response is not object';
// show warning in debug window
node.warn(msg.error.message);
// set node status
setNodeStatus('warning');
}
// handle object response
else if(response && typeof response === 'object') {
// capture headers
if(response && typeof response.headers === 'object') {
msg.headers = response.headers;
if(response.hasOwnProperty('headers') && typeof response.headers === 'object') {
newMsg.headers = response.headers;
}
// capture status
if(response && response.status) {
msg.status = response.status;
if(response.hasOwnProperty('status')) {
newMsg.status = response.status;
} else {
newMsg.status = 500;
}
// handle non 2xx status
if(msg.status > 299) {
processError(response);
return;
}
// if response.data
if(response.hasOwnProperty('data')) {
var data = response.data;
// if empty response
if(data === '' || data === '{}') {
// if response is from a delete...
if(newMsg.status === 204) {
newMsg.payload = {};
// determine if empty respose is from delete
if(response.status && response.status === 204) {
// set msg.error to null
msg.error = null;
} else {
// set error message
msg.error.message = 'response is empty';
// send message
node.send(newMsg);
// show warning in debug window
node.warn(msg.error.message);
return null;
}
// set node status
setNodeStatus('warning');
}
// if empty response...
if(typeof data === 'string' && (data === '' || data === '{}')) {
// show warning
node.warn('error processsing response');
// set node status
setNodeStatus('warning');
return null;
}
// if response.data is string
else if(typeof data === 'string') {
// if response.data is valid...
if(typeof data === 'string') {
// attempt parsing of json

@@ -227,233 +199,104 @@ try {

catch(err) {
// set error message
msg.error.message = 'json data not found in response';
// show warning
node.warn('error: ' + err.message || 'undefined');
// show warning in debug window
node.warn(msg.error.message);
// set node status
setNodeStatus('warning');
// set msg.topic
msg.topic = node.resource + '.' + node.method;
// send message
node.send(msg);
return;
return null;
}
// abstract away items container for array response
if(data && data.hasOwnProperty('items') && data.items instanceof Array) {
msg.payload = data.items;
} else {
msg.payload = data;
}
// if response is array of items
if(typeof data === 'object' && data.hasOwnProperty('items') && data.items instanceof Array) {
// set msg.error to null
msg.error = null;
}
}
// if as multiple messages
if(true) {
// define _msgid
newMsg._msgid = RED.util.generateId();
// else, response.data does not exist...
else {
// set error message
msg.error.message = 'response has no content';
// define messages
var newMsgArray = _.map(data.items, function(item, index) {
var msg = _.cloneDeep(newMsg);
// show warning in debug window
node.warn(msg.error.message);
msg.payload = item;
msg.parts = {
"id": msg._msgid,
"type": "array",
"index": index,
"count": data.items.length
};
return msg;
});
// set node status
setNodeStatus('warning');
}
}
// send message
node.send([newMsgArray]);
}
// set msg.topic
msg.topic = node.resource + '.' + node.method;
// else, as single message
else {
// define _msgid
newMsg._msgid = msg._msgid;
// send msg
node.send(msg);
}
// define paypload
newMsg.payload = data.items;
// api validation error
function processError(msg, error) {
// extend msg object
msg.payload = {};
msg.error = {};
msg.status = 500;
// send message
node.send(newMsg);
}
// handle undefined error
if(typeof error === 'undefined') {
// set error message
msg.error.message = 'unknown error';
// show warning in debug window
node.warn(msg.error.message);
// set node status
setNodeStatus('error');
}
// handle string error
else if(error && typeof error === 'string') {
// set error message
msg.error.message = error;
// show warning in debug window
node.warn(msg.error.message);
// set node status
setNodeStatus('error');
}
// handle object response
else if(error && typeof error === 'object') {
// capture headers
if(error && typeof error.headers === 'object') {
msg.headers = error.headers;
}
// capture status
if(error && error.status) {
msg.status = error.status;
}
// check for error object from Spark API
if(error && error.hasOwnProperty('data')) {
var data = error.data;
// if empty response
if(data === '' || data === '{}') {
// set error message
msg.error.message = 'unknown error';
// show warning in debug window
node.warn(msg.error.message);
// set node status
setNodeStatus('error');
}
// if error.data is string
else if(typeof data === 'string') {
// atempt parsing of json
try {
var data = JSON.parse(data);
}
catch(err) {
// set error message
msg.error.message = 'unknown error';
// show warning in debug window
node.warn(msg.error.message);
// else, response is single item
else {
newMsg.payload = data;
// set node status
setNodeStatus('error');
// set msg.topic
msg.topic = node.resource + '.' + node.method;
// send message
node.send(msg);
return;
node.send(newMsg);
}
// msg.error.message
if(data.hasOwnProperty('message')) {
msg.error.message = data.message;
} else {
// define errors that do not generate message
switch(msg.status) {
case 400:
// set error message
msg.error.message = '400 (request invalid) response received';
break;
case 401:
// set error message
msg.error.message = '401 (request unauthorized) response received';
break;
case 403:
// set error message
msg.error.message = '403 (request not allowed) response received';
break;
case 404:
// set error message
msg.error.message = '404 (request unauthorized) response received';
break;
case 429:
// set error message
msg.error.message = '429 (rate limiter hit) response received';
break;
case 500:
// set error message
msg.error.message = '500 (something went wrong on server) response received';
break;
case 501:
// set error message
msg.error.message = '501 response received';
break;
case 502:
// set error message
msg.error.message = '502 response received';
break;
case 503:
// set error message
msg.error.message = '503 (server is overloaded) response received';
break;
default:
// set error message
msg.error.message = msg.status + ' response received';
break;
}
}
// msg.error.description
if(data.hasOwnProperty('errors') && data.errors instanceof Array) {
if(data.errors.length > 0 && data.errors[0].hasOwnProperty('description')) {
msg.error.description = data.errors[0].description;
}
}
// msg.error.trackingId
if(data.hasOwnProperty('trackingId')) {
msg.error.trackingId = data.trackingId;
}
// show warning in debug window
node.warn(msg.error.message);
// set node status
setNodeStatus('error');
}
}
// else error.data does not exist
// else, response.data does not exist...
else {
// set error message
msg.error.message = 'unknown error';
// show warning
node.warn('response has no content');
// show warning in debug window
node.warn(msg.error.message);
// set node status
setNodeStatus('error');
setNodeStatus('warning');
}
}
// set msg.topic
msg.topic = node.resource + '.' + node.method;
else {
node.warn('response invalid or not received');
// send message
node.send(msg);
// set node status
setNodeStatus('warning');
}
}
function processError(err) {
if(err && typeof err === 'object' && err.hasOwnProperty('message')) {
// show error
node.error(err.message);
} else {
node.error('unknown error');
}
// set node status
setNodeStatus('error');
}
// start node if profileConfig is defined
if(node.profileConfig) {
// set default status
setNodeStatus();
// create client
createClient(node.apiUrl, function(err) {
// if error creating client
if(err) {
// show warning in debug window
// show warning
node.warn('invalid input');

@@ -465,5 +308,8 @@

return;
} else {
}
// input event
// else, client created
else {
// create input event
node.on('input', function(msg) {

@@ -484,3 +330,3 @@

if(params instanceof Array) {
// show warning in debug window
// show warning
node.warn('invalid input');

@@ -495,3 +341,3 @@

catch(err) {
// show warning in debug window
// show warning
node.warn(err.message);

@@ -513,3 +359,3 @@

else {
// show warning in debug window
// show warning
node.warn('invalid input');

@@ -524,3 +370,2 @@

// Send Spark API Request
reqPing();
sendRequest(msg, node.resource, node.method, params, opts, node.profileConfig.credentials.token, processResponse, processError);

@@ -527,0 +372,0 @@ });

@@ -10,76 +10,46 @@ module.exports = function(RED) {

node.parser = n.parser;
node.output = n.output.toLowerCase();
node.topic = n.topic;
node.parsers = n.parsers;
function processPayload(pl, k) {
// determine topic
var topic = typeof node.topic === 'string' && node.topic.length > 0 ? node.topic : k;
// return value
function getPayloadKeyValue(payload, key) {
// validate payload is object
if(typeof payload === 'object') {
// value
var value = '';
var value = '';
// validate payload is object
if(typeof pl === 'object') {
// if webhook
if(pl.hasOwnProperty('data')) {
if(pl.data.hasOwnProperty(k)) {
value = pl.data[k];
} else {
return null;
}
// search msg.payload.data
if(payload.hasOwnProperty('data') && payload.data.hasOwnProperty(key)) {
value = payload.data[key];
}
// else api
else {
if(pl.hasOwnProperty(k)) {
value = pl[k];
} else {
return null;
}
// search msg.payload
else if(payload.hasOwnProperty(key)) {
value = payload[key];
}
// format output
// if output is original
if(node.output === 'original') {
if(value != '') {
return value;
}
// else, if output is object
else if(node.output === 'object') {
return { [topic]: value };
}
// else, unrecognized
else {
return null;
}
}
// else, not an object
else {
return null;
}
}
function processPayloadArray(plArr, k) {
// determine topic
var topic = typeof node.topic === 'string' && node.topic.length > 0 ? node.topic : k;
function processMsg(msg, parsers) {
var newMsg = _.cloneDeep(msg);
var newPayload = {};
var collection = _.map(plArr, function(pl) {
// define payload
var payload = processPayload(pl, k);
if(payload) {
return { "payload": payload, "topic": topic };
}
});
if(parsers && parsers instanceof Array && parsers.length > 0) {
_.forEach(parsers, function(parser) {
if(parser.hasOwnProperty('key') && parser.key !== '') {
if(!parser.hasOwnProperty('as') || (parser.hasOwnProperty('as') && parser.as === '')) {
parser.as = parser.key;
}
newPayload[parser.as] = getPayloadKeyValue(newMsg.payload, parser.key);
}
});
}
collection = _.compact(collection);
if(newPayload !== {}) {
newMsg.payload = newPayload ;
if(collection && collection.length > 0) {
return collection;
} else {
return null;
return newMsg;
}

@@ -90,38 +60,18 @@ }

node.on('input', function(msg) {
// determine topic
var topic = typeof node.topic === 'string' && node.topic.length > 0 ? node.topic : node.parser;
var msgParser = {};
// if input is valid
if(typeof node.parser === 'string' && msg && typeof msg === 'object' && msg.hasOwnProperty('payload')) {
var payload = _.clone(msg.payload);
if(msg && typeof msg === 'object' && msg.hasOwnProperty('payload')) {
// if payload is array
if(payload instanceof Array) {
if(payload.length > 0) {
var newArray = processPayloadArray(payload, node.parser);
if(newArray && newArray.length > 0) {
msgParser = newArray;
} else {
msgParser = null;
node.warn('unmatched parser');
}
}
}
// create new message and preserve properties that this node is not manipulating
var newMsg = _.cloneDeep(msg);
// else, payload is not instance of array
else {
var newPayload = processPayload(payload, node.parser);
if(newPayload) {
msgParser.payload = newPayload;
msgParser.topic = topic;
} else {
msgParser = null;
node.warn('unmatched parser');
// if payload is object
if(typeof newMsg.payload === 'object') {
newMsg = processMsg(newMsg, node.parsers);
// if parser returned value
if(newMsg) {
node.send(newMsg);
}
}
// send msgs
node.send([msgParser, msg]);
}

@@ -132,5 +82,2 @@

node.warn('invalid input');
// send msgs
node.send([null, msg]);
}

@@ -137,0 +84,0 @@ });

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc