Update CSV node to support Property In/Out

This commit is contained in:
Steve-Mcl 2024-04-12 11:02:06 +01:00
parent 2621a3a628
commit 4874e64387
2 changed files with 61 additions and 41 deletions

View File

@ -1,5 +1,13 @@
<script type="text/html" data-template-name="csv">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div class="form-row">
<label for="node-input-property"><i class="fa fa-sign-in"></i> <span data-i18n="common.label.propertyIn"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label for="node-input-temp"><i class="fa fa-list"></i> <span data-i18n="csv.label.columns"></span></label>
<input type="text" id="node-input-temp" data-i18n="[placeholder]csv.placeholder.columns">
@ -32,8 +40,8 @@
</div>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
<label for="node-input-propertyOut"><i class="fa fa-sign-out"></i> <span data-i18n="common.label.propertyOut"></span></label>
<input type="text" id="node-input-propertyOut" style="width:70%;"/>
</div>
<hr align="middle"/>
<div class="form-row">
@ -102,7 +110,13 @@
skip: {value:"0"},
strings: {value:true},
include_empty_strings: {value:""},
include_null_values: {value:""}
include_null_values: {value:""},
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }),
label:RED._("node-red:common.label.propertyIn")},
propertyOut: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true}),
label:RED._("node-red:common.label.propertyOut")}
},
inputs:1,
outputs:1,

View File

@ -26,6 +26,9 @@ module.exports = function(RED) {
node.status({}) // clear status
node.property = n.property||"payload";
node.propertyOut = n.propertyOut||node.property;
if (legacyMode) {
this.template = (n.temp || "");
this.sep = (n.sep || ',').replace(/\\t/g,"\t").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
@ -66,43 +69,44 @@ module.exports = function(RED) {
if (msg.hasOwnProperty("reset")) {
node.hdrSent = false;
}
if (msg.hasOwnProperty("payload")) {
if (typeof msg.payload == "object") { // convert object to CSV string
if (msg.hasOwnProperty(node.property)) {
let inputData = RED.util.getMessageProperty(msg, node.property)
if (typeof inputData == "object") { // convert object to CSV string
try {
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
template = clean(node.template);
}
const ou = [];
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
if (!Array.isArray(inputData)) { inputData = [ inputData ]; }
if (node.hdrout !== "none" && node.hdrSent === false) {
if ((template.length === 1) && (template[0] === '')) {
if (msg.hasOwnProperty("columns")) {
template = clean(msg.columns || "",",");
}
else {
template = Object.keys(msg.payload[0]);
template = Object.keys(inputData[0]);
}
}
ou.push(template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep));
if (node.hdrout === "once") { node.hdrSent = true; }
}
for (var s = 0; s < msg.payload.length; s++) {
if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
for (var t = 0; t < msg.payload[s].length; t++) {
if (msg.payload[s][t] === undefined) { msg.payload[s][t] = ""; }
if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""');
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
for (var s = 0; s < inputData.length; s++) {
if ((Array.isArray(inputData[s])) || (typeof inputData[s] !== "object")) {
if (typeof inputData[s] !== "object") { inputData = [ inputData ]; }
for (var t = 0; t < inputData[s].length; t++) {
if (inputData[s][t] === undefined) { inputData[s][t] = ""; }
if (inputData[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
inputData[s][t] = inputData[s][t].toString().replace(/"/g, '""');
inputData[s][t] = node.quo + inputData[s][t].toString() + node.quo;
}
else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
else if (inputData[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
inputData[s][t] = node.quo + inputData[s][t].toString() + node.quo;
}
else if (msg.payload[s][t].toString().indexOf("\n") !== -1) { // add quotes if any "\n"
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
else if (inputData[s][t].toString().indexOf("\n") !== -1) { // add quotes if any "\n"
inputData[s][t] = node.quo + inputData[s][t].toString() + node.quo;
}
}
ou.push(msg.payload[s].join(node.sep));
ou.push(inputData[s].join(node.sep));
}
else {
if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) {
@ -115,16 +119,16 @@ module.exports = function(RED) {
tmpwarn = false;
}
const row = [];
for (var p in msg.payload[0]) {
for (var p in inputData[0]) {
/* istanbul ignore else */
if (msg.payload[s].hasOwnProperty(p)) {
if (inputData[s].hasOwnProperty(p)) {
/* istanbul ignore else */
if (typeof msg.payload[s][p] !== "object") {
if (typeof inputData[s][p] !== "object") {
// Fix to honour include null values flag
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
//if (typeof inputData[s][p] !== "object" || (node.include_null_values === true && inputData[s][p] === null)) {
var q = "";
if (msg.payload[s][p] !== undefined) {
q += msg.payload[s][p];
if (inputData[s][p] !== undefined) {
q += inputData[s][p];
}
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
q = q.replace(/"/g, '""');
@ -149,7 +153,7 @@ module.exports = function(RED) {
var tt = template[t];
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
else { tt = '"'+tt+'"'; }
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
var p = RED.util.getMessageProperty(msg, node.property + '["'+s+'"]['+tt+']');
/* istanbul ignore else */
if (p === undefined) { p = ""; }
// fix to honour include null values flag
@ -170,16 +174,17 @@ module.exports = function(RED) {
}
}
// join lines, don't forget to add the last new line
msg.payload = ou.join(node.ret) + node.ret;
inputData = ou.join(node.ret) + node.ret;
RED.util.setMessageProperty(msg, node.propertyOut, inputData)
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(',');
if (msg.payload !== '') {
if (inputData !== '') {
send(msg);
}
done();
}
catch(e) { done(e); }
}
else if (typeof msg.payload == "string") { // convert CSV string to object
else if (typeof inputData == "string") { // convert CSV string to object
try {
var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
var j = 0; // pointer into array of template items
@ -188,7 +193,7 @@ module.exports = function(RED) {
var a = []; // output array is needed for multiline option
var first = true; // is this the first line
var last = false;
var line = msg.payload;
var line = inputData;
var linecount = 0;
var tmp = "";
var has_parts = msg.hasOwnProperty("parts");
@ -282,13 +287,13 @@ module.exports = function(RED) {
}
if (node.multi !== "one") {
msg.payload = a;
inputData = a;
if (has_parts && nocr <= 1) {
if (JSON.stringify(o) !== "{}") {
node.store.push(o);
}
if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store;
inputData = node.store;
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
delete msg.parts;
send(msg);
@ -305,7 +310,7 @@ module.exports = function(RED) {
for (var i = 0; i < len; i++) {
var newMessage = RED.util.cloneMessage(msg);
newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
newMessage.payload = a[i];
RED.util.setMessageProperty(newMessage, node.propertyOut, a[i])
if (!has_parts) {
newMessage.parts = {
id: msg._msgid,
@ -411,8 +416,8 @@ module.exports = function(RED) {
if (msg.hasOwnProperty("reset")) {
node.hdrSent = false
}
if (msg.hasOwnProperty("payload")) {
let inputData = msg.payload
if (msg.hasOwnProperty(node.property)) {
let inputData = RED.util.getMessageProperty(msg, node.property)
if (typeof inputData == "object") { // convert object to CSV string
try {
// first determine the payload kind. Array or objects? Array of primitives? Array of arrays? Just an object?
@ -517,9 +522,10 @@ module.exports = function(RED) {
}
// join lines, don't forget to add the last new line
msg.payload = stringBuilder.join(node.ret) + node.ret
const result = stringBuilder.join(node.ret) + node.ret
RED.util.setMessageProperty(msg, node.propertyOut, result)
msg.columns = templateArrayToColumnString(template)
if (msg.payload !== '') { send(msg) }
if (result !== '') { send(msg) }
done()
}
catch (e) {
@ -614,7 +620,7 @@ module.exports = function(RED) {
node.store.push(...data)
}
if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store
RED.util.setMessageProperty(msg, node.propertyOut, node.store)
msg.columns = csvParseResult.header
// msg._mode = 'RFC4180 mode'
delete msg.parts
@ -625,7 +631,7 @@ module.exports = function(RED) {
else {
msg.columns = csvParseResult.header
// msg._mode = 'RFC4180 mode'
msg.payload = data
RED.util.setMessageProperty(msg, node.propertyOut, data)
send(msg); // finally send the array
}
}
@ -634,7 +640,7 @@ module.exports = function(RED) {
for (let row = 0; row < len; row++) {
const newMessage = RED.util.cloneMessage(msg)
newMessage.columns = csvParseResult.header
newMessage.payload = data[row]
RED.util.setMessageProperty(newMessage, node.propertyOut, data[row])
if (!has_parts) {
newMessage.parts = {
id: msg._msgid,