node-red-contrib-prib-functions
Version:
Node-RED added node functions.
582 lines (578 loc) • 27.4 kB
JavaScript
const logger = new (require("node-red-contrib-logger"))("transform");
logger.sendInfo("Copyright 2020 Jaroslav Peter Prib");
const NumPy = require("./NumPy.js")
const typedInput = require("../lib/typedInput.js")
const {coalesce,nullif,toDateTypeZulu}=require("../lib/objectExtensions")
const regexCSV=/,(?=(?:(?:[^"]*"){2})*[^"]*$)/,
Buffer=require('buffer').Buffer,
os=require('os'),
path=require('path'),
process=require('process');
let avsc,snappy,xmlParser,json2xmlParser,XLSX,compressor;
const {ISO8583BitMapId,ISO8583BitMapName}=require("./ISO8583BitMap");
const { callbackify } = require("util");
let ISO8583,ISO8583message;
const XMLoptions = {
// attributeNamePrefix : "@_",
// attrNodeName: "attr", //default is 'false'
// textNodeName : "#text",
ignoreAttributes : false,
ignoreNameSpace : false,
allowBooleanAttributes : true,
parseNodeValue : true,
parseAttributeValue : false,
// cdataTagName: "__cdata", //default is 'false'
// cdataPositionChar: "\\c",
// parseTrueNumberOnly: false,
// arrayMode: false, //"strict"
// attrValueProcessor: (val, attrName) => he.decode(val, {isAttributeValue: true}),//default is a=>a
// tagValueProcessor : (val, tagName) => he.decode(val), //default is a=>a
// stopNodes: ["parse-me-as-string"]
trimValues: false
};
const makeDateType=data=>(data instanceof Date? data:new Date(data))
const error=(node,ex,shortMessage)=>{
if(!ex) ex =Error("no exception/error")
if(logger.active) logger.send({label:"transformNode catch",shortMessage:shortMessage,error:ex.message,stack:ex.stack});
node.error(ex.message);
node.status({fill:"red",shape:"ring",text:(shortMessage??ex.message??"unknown error").substr(0,50)});
}
const getAvroTransformer=(node,schema)=>{
try{
return avsc.Type.forSchema(node.schemas[schema])
} catch(ex){
if(node.schemas.hasOwnProperty(schema)) throw ex
throw Error("schema not found for "+schema)
}
}
const xlsx2=require("./xlsx2")
const ConfluenceToJSON=(RED,node,msg,data,callback)=>{
if(!Buffer.isBuffer(data)) data=Buffer.from(data)
const magicByte=data.readUInt8()
if(magicByte!==0) throw Error("expected magic byte and not found, found "+magicByte)
if(data.length<5) throw Error("missing schema, data length "+data.length)
const schema=data.readInt32BE(1)
const avroTransformer=getAvroTransformer(node,schema)
callback(RED,node,msg,data,{schema:schema,data:avroTransformer.fromBuffer(data.subarray(5))})
}
const JSONToConfluence=(RED,node,msg,data,callback)=>{
if(!data.schema) throw Error("property schema not defined");
if(!data.data) throw Error("property data not defined");
const header=Buffer.alloc(5);
header[0]=0;
header.writeInt32BE(data.schema, 1);
const transformer=getAvroTransformer(node,data.schema);
const avro=transformer.toBuffer(data.data);
callback(RED,node,msg,data,Buffer.concat([header,avro]))
}
function SendArray(RED,node,msg,array){
if(logger.active) logger.send({label:"SendArray",size:array.length});
this.index=0;
this.RED=RED;
this.node=node;
this.msg=msg;
this.array=array;
this.next();
}
SendArray.prototype.next=function() {
this.usageCPU=process.cpuUsage(this.usageCPU);
this.resourceUsage=process.resourceUsage();
const memoryUsage=process.memoryUsage(),
heapUsedRatio=memoryUsage.heapUsed/memoryUsage.heapTotal,
memoryUsedRatio=os.freemem()/os.totalmem();
currentTime=Date.now(),
cpuUsedRatio=(this.usageCPU.user+this.usageCPU.system)/((currentTime-this.lastTouchTime)*100000);
if(logger.active) logger.send({label:"SendArray.next",index:this.index,cpuUsedRatio:cpuUsedRatio,memoryUsedRatio:memoryUsedRatio,heapUsedRatio:heapUsedRatio});
this.lastTouchTime=currentTime;
let i=cpuUsedRatio>0.9 || memoryUsedRatio>0.9 || heapUsedRatio>0.99?1:100;
const newMsgs=[]
while(--i) {
if(this.index>=this.array.length) {
delete this;
return;
}
const newMsg=this.RED.util.cloneMessage(this.msg),index=this.index;
newMsg._msgid=newMsg._msgid+":"+index;
this.node.setData(this.RED,this.node,newMsg,this.array[index],index)
this.index++;
newMsgs.push(newMsg);
}
this.node.send([newMsgs]);
const call=this.next.bind(this);
this.timeoutID=setTimeout(call, 100);
};
const removeQuotes=(data)=>{
try{
const d=data.trim();
if(d.length>1 && d.startsWith('"') && d.endsWith('"')) return d.slice(1,-1);
const r=Number(d);
return r?r:d;
} catch(ex) {
return data;
}
}
const csvLines=(data,skipLeading=0,skipTrailing=0)=>{
if(logger.active) logger.send({label:"csvLines",skipLeading:skipLeading,skipTrailing:skipTrailing});
let lines=data.split(/[\r\n]+/g),skip=skipLeading;
while(skip--) lines.shift();
skip=skipTrailing;
while(skip--) lines.pop();
return lines;
}
const array2tag=(a,t,tf)=>{
const ts="<"+t+">",te="</"+t+">"
return a.reduce((a,c)=>a+=ts+tf(c)+te,"");
}
const Array2csv=(node,data)=>{
if(!(data instanceof Array)) return JSON.stringify(data);
if(data.length==0) return;
if(data[0] instanceof Array) {
return data.map(r=>
r instanceof Array?r.map(c=>JSON.stringify(c)).join(node.delimiter):JSON.stringify(r)
).join("\n");
} else if(data[0] instanceof Object) {
const properties=[];
data.forEach(r=>{
if(typeof r == "object" && ! (r instanceof Array)){
Object.keys(r).forEach(p=>{
if(properties.includes(p)) return
properties.push(p)
})
}
})
properties.sort();
const rows=data.map(r=>{
if(typeof r == "object" && ! (r instanceof Array)) {
return properties.map(c=>r[c]||"").join(node.delimiter)
} else {
return node.delimiter.repeat(properties.length)+JSON.stringify(r)
}
})
return properties.join(node.delimiter)+"\n"+rows.join("\n");
}
return data.map(r=>JSON.stringify(r)).join("/n");
}
const JSON2Array=data=>{
if(data instanceof Object){
let a=[];
properties=Object.keys(data)
for(const p of properties) {
a.push([p,JSON2Array(data[p])]);
}
return a;
}
return [data]
}
const JSON2HTML=(data,level=0)=>{
if(Array.isArray(data)) {
return data.length?"<table><tr>"+data.map((r)=>JSON2HTML(r,++level)).join("</tr><tr>")+"</tr><table>":"";
}
if(data instanceof Object){
let a=[];
for(let p in data) {
a.push("<td style='vertical-align: top;'>"+escape(p)+":</td><td>"+functions.JSONToHTML(data[p],++level)+"</td>");
}
return "<table><tr>"+a.join("</tr><tr>")+"</tr><table>";
}
return escape(data);
}
const functions={
ArrayToCSV: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.map(c=>JSON.stringify(c)).join("\n")),
ArrayToHTML: (RED,node,msg,data,callback)=>callback(RED,node,msg,
"<table>"+ array2tag(data,"tr",(c)=>(
Array.isArray(c)?
array2tag(c,"td",(cc)=>
Array.isArray(cc)?
functions.ArrayToHTML(RED,node,msg,cc):
"<![CDATA["+cc+"]]>"
):
"<![CDATA["+c+"]]>"
)
)+"</table>"),
ArrayToISO8385: (RED,node,msg,data,callback)=>callback(RED,node,msg,ISO8583message.packSync(data)),
ArrayToMessages: (RED,node,msg,data,callback)=>{
if(logger.active) logger.send({label:"ArrayToMessages",arraySize:data.length});
if(data.length>node.maxMessages) throw Error("messages to be created "+data.length +"> max: "+node.maxMessages);
const newMsgs=[]
data.map((row,i)=>{
const newMsg=RED.util.cloneMessage(msg);
newMsg._msgid=newMsg._msgid+":"+i;
if(logger.active) logger.send({label:"ArrayToMessages",row:row,index:i});
node.setData(RED,node,newMsg,row,i)
newMsgs.push(newMsg);
});
node.send([newMsgs])
},
ArrayToXLSX:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.Array2XLSX(data)),
ArrayToXLSXObject:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.Array2XLSXObject(data)),
AVROToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,node.avroTransformer.fromBuffer(data)), // = {kind: 'CAT', name: 'Albert'}
BigIntToRangeLimit: (RED,node,msg,data,callback)=> callback(RED,node,msg,data?data.rangeLimit(node.minBigIntTyped,node.maxBigIntTyped):node.minBigInt),
BufferToCompressed: (RED,node,msg,data,callback)=>compressor.compress(data,
compressed=>node.setData(RED,node,msg,compressed,callback),
err=>error(node,Error(err))
),
CompressedToBuffer:(RED,node,msg,data,callback)=>compressor.decompress(data,
uncompressed=>node.setData(RED,node,msg,uncompressed,callback),
err=>error(node,Error(err))
),
CompressedToJSON:(RED,node,msg,data,callback)=>compressor.decompress(data,
uncompressed=>{
try{
node.setData(RED,node,msg,JSON.parse(uncompresse,callback));
} catch(ex){
msg.error=ex.message
node.setData(RED,node,msg,uncompressed,callback);
}
},
err=>error(node,Error(err))
),
CompressedToString:(RED,node,msg,data,callback)=>compressor.decompress(data,
uncompressed=>{
try{
node.setData(RED,node,msg,uncompressed.toString(),callback)
} catch(ex){
msg.error=ex.message;
node.setData(RED,node,msg,uncompressed,callback)
}
},
err=>error(node,Error(err))
),
ConfluenceToJSON: ConfluenceToJSON,
CSVToArray: (RED,node,msg,data,callback)=>{
let lines=csvLines(data,node.skipLeading,node.skipTrailing);
lines.forEach((value, idx) => {
lines[idx]=value.split(regexCSV).map((c)=>removeQuotes(c));
});
callback(RED,node,msg,lines)
},
CSVToHTML: (RED,node,msg,data,callback)=>callback(RED,node,msg,
"<table>"+ array2tag(csvLines(data,node.skipLeading,node.skipTrailing),"tr",(line)=>
array2tag(line.split(regexCSV),"td",(c)=>"<![CDATA["+removeQuotes(c)+"]]>")
)+"</table>"),
CSVToMessages: (RED,node,msg,data,callback)=>functions.ArrayToMessages(RED,node,msg,csvLines(data,this.skipLeading,this.skipTrailing),callback),
CSVWithHeaderToArray: (RED,node,msg,data,callback)=>{
let r=functions.CSVToArray(RED,node,msg,data);
r.shift();
rcallback(RED,node,msg,r)
},
CSVWithHeaderToHTML: (RED,node,msg,data,callback)=>{
let lines=csvLines(data,node.skipLeading,node.skipTrailing);
const header=array2tag(lines.shift().split(regexCSV),"th",(c)=>"<![CDATA["+removeQuotes(c)+"]]>");
const result= "<table><tr>"+header+"</tr>"+array2tag(lines,"tr",(line)=>
array2tag(line.split(regexCSV),'td',(c)=>"<![CDATA["+removeQuotes(c)+"]]>")
)+"</table>"
callback(RED,node,msg,result)
},
CSVWithHeaderToJSON: (RED,node,msg,data,callback)=>{
let lines=csvLines(data,node.skipLeading,node.skipTrailing);
let header=lines.shift().split(regexCSV);
if(logger.active) logger.send({label:"CSVWithHeaderToJSON",header:header});
lines.forEach((value, idx) => {
let o={};
value.split(regexCSV).forEach((c,i)=>{
o[header[i]]=removeQuotes(c);
});
lines[idx]=o;
});
callback(RED,node,msg,lines)
},
DateToisBetween: (RED,node,msg,data,callback)=> callback(RED,node,msg,toDateTypeZulu(data).isBetween(node.minDateTyped,node.maxDateTyped)),
DateToISODate: (RED,node,msg,data,callback)=> callback(RED,node,msg,toDateTypeZulu(data).toISOString().slice(0,10)),
DateToLocalDate: (RED,node,msg,data,callback)=> callback(RED,node,msg,toDateTypeZulu(data).toLocaleDateString().slice(0,10)),
DateToRangeLimit: (RED,node,msg,data,callback)=> callback(RED,node,msg,(data? toDateTypeZulu(data).rangeLimit(node.minDateTyped,node.maxDateTyped):node.minDateTyped)),
ISO8385ToArray: (RED,node,msg,data,callback)=>callback(RED,node,msg,ISO8583message.unpackSync(data, data.length)),
ISO8385ToJSON: (RED,node,msg,data,callback)=>{
let j={},d=ISO8583message.unpackSync(data, data.length);
d.forEach((r)=>{
j[ISO8583BitMapId(r[0]).name]=r[1];
});
callback(RED,node,msg,r)
},
JSONToArray: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON2Array(data)),
JSONToAVRO: (RED,node,msg,data,callback)=>callback(RED,node,msg,node.avroTransformer.toBuffer(data)), // Encoded buffer.
JSONToCompressed: (RED,node,msg,data,callback)=>compressor.compress(JSON.stringify(data),
compressed=>node.setData(RED,node,msg,compressed.callback),
err=>error(node,Error(err))
),
JSONToConfluence:JSONToConfluence,
JSONToCSV: (RED,node,msg,data,callback)=>callback(RED,node,msg,Array2csv(node,data)),
JSONToHTML: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON2HTML(data)),
JSONToISO8385: (RED,node,msg,data,callback)=>{
var d=[];
Object.getOwnPropertyNames(data).forEach((v)=>d.push([ISO8583BitMapName[v].id,data[v]]));
d.sort((a, b) => a[0] - b[0]);
callback(RED,node,msg, ISO8583message.packSync(d))
},
JSONToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,data),
JSONToMessages: (RED,node,msg,data,callback)=>{
if(logger.active) logger.send({label:"JSONToMessages",messages:data.length});
if(Array.isArray(data)) {
new node.SendArray(RED,node,msg,data);
} else {
const newMsg=RED.util.cloneMessage(msg);
newMsg._msgid=newMsg._msgid+":0";
node.setData(RED,node,newMsg,data,callback)
}
},
JSONTonpy: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data).toNpyBuffer()),
JSONToNumPyObject: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data)),
JSONToString: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON.stringify(data)),
JSONToXLSX:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.JSON2XLSX(data)),
JSONToXLSXObject:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.JSON2XLSXObject(data)),
JSONToXML: (RED,node,msg,data,callback)=>callback(RED,node,msg,json2xmlParser.parse(data)),
npyToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data).toSerializable()),
npyToNumPyObject: (RED,node,msg,data,callback)=>callback(RED,node,msg,new NumPy(data)),
NumPyObjectToJSON: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toSerializable()),
NumberToAbbreviated: (RED,node,msg,data,callback)=> callback(RED,node,msg,data?data.isAbbreviated():data),
NumberToisBetween: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.isBetween(node.minNumber,node.maxNumber)),
NumberToRangeLimit: (RED,node,msg,data)=> callback(RED,node,msg,data?data.rangeLimit(node.minNumber,node.maxNumber):node.minNumber),
ObjectToCoalesce: (RED,node,msg,data,callback)=>callback(RED,node,msg,coalesce(data,node.value)),
ObjectToDeepClone: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.deepClone()),
ObjectToNullif: (RED,node,msg,data,callback)=>callback(RED,node,msg,nullif(data,node.value)),
StringToAppend: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.concat(node.getString(msg))),
StringToArrayByDelimiter: (RED,node,msg,data,callback)=>callback(RED,node,msg,data
.split(node.delimiter??',')
.map(entry => entry.trim())
.filter(entry => entry)),
StringToAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.At(node.index)),
StringToCapitalize: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.capitalize()),
StringToCamelize: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase())),
StringToCharAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.charAt(node.index)),
StringToCharCodeAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.charCodeAt(node.index)),
StringToCodePointAt: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.codePointAt(node.index)),
StringToCompressed: (RED,node,msg,data,callback)=>compressor.compress(data,
compressed=>node.setData(RED,node,msg,compressed,callback),
err=>error(node,Error(err))
),
StringToConcat: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.concat(node.getString(msg))),
StringToDate: (RED,node,msg,data,callback)=>callback(RED,node,msg,toDateTypeZulu(data)),
StringToDateLocal: (RED,node,msg,data,callback)=>callback(RED,node,msg,new Date(data)),
StringToTimestamp: (RED,node,msg,data,callback)=>callback(RED,node,msg,Date.parse(data)),
StringToDelimiterOnCase:(RED,node,msg,data,callback)=>callback(RED,node,msg,
data.replace(/[A-Z]/g, (letter, index) => {
const lcLet = letter.toLowerCase();
const separator= node.delimiter??"-"
return index ? separator + lcLet : lcLet;
})
.replace(/([-_ ]){1,}/g,node.delimiter??"-")
),
StringToDeunderscore: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.deunderscore()),
StringToDeunderscoreCapitilize: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.deunderscoreCapitilize()),
StringToDropSquareBracketPrefix: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.dropSquareBracketPrefix()),
StringToEndsWith: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.endsWith(node.getString(msg))),
StringToFloat: (RED,node,msg,data,callback)=>callback(RED,node,msg,parseFloat(data)),
StringToGetWord: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.getWord(node.index)),
StringToInteger: (RED,node,msg,data,callback)=>callback(RED,node,msg,parseInt(data, node.radix??10)),
StringToisBetween: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.isBetween(node.minString,node.maxString)),
StringToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,JSON.parse(data)),
StringToLowerCase: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toLowerCase()),
StringToNumber: (RED,node,msg,data,callback)=>callback(RED,node,msg,Number(data)),
StringToPrepend: (RED,node,msg,data,callback)=>callback(RED,node,msg,node.getString(msg).concat(data)),
StringToRangeLimit: (RED,node,msg,data,callback)=> callback(RED,node,msg,data?data.rangeLimit(node.minString,node.maxString):node.minString),
StringToReal: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toReal()),
StringToSplit: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.split(node.getString(msg))),
StringToStartsWith: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.startsWith(node.getString(msg))),
StringToTitle: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.toTitle()),
StringTotTitleGrammatical: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.toTitleGrammatical()),
StringToTrim: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.trim()),
StringToTrimEnd: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.trimEnd()),
StringToTrimStart: (RED,node,msg,data,callback)=>callback(RED,node,msg,data.trimStart()),
StringToUpperCase: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.toUpperCase()),
StringToXmlStringEncode: (RED,node,msg,data,callback)=> callback(RED,node,msg,data.xmlStringEncode()),
pathToBasename: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.basename(data)),
pathToDirname: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.dirname(data)),
pathToExtname: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.extname(data)),
pathToFormat: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.format(data)),
pathToIsAbsolute: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.isAbsolute(data)),
pathToJoin: (RED,node,msg,data)=>callback(RED,node,msg,path.join(data)),
pathToParse: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.parse(data)),
pathToNormalize: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.normalize(data)),
pathToResolve: (RED,node,msg,data,callback)=>callback(RED,node,msg,path.resolve(data)),
snappyToCompress: (RED,node,msg,data,callback)=>{
if(logger.active) logger.send({label:"snappyToCompress"});
snappy.compress(data, (err, data)=>{
if(logger.active) logger.send({label:"snappy.compress",error:err})
if(err) return error(node,Error(err))
node.setData(RED,node,msg,data,callback)
})
},
snappyToUncompress: (RED,node,msg,data,callback)=>{
if(logger.active) logger.send({label:"snappyToUncompress"});
snappy.uncompress(data, { asBuffer: false }, (err, data)=>{
if(logger.active) logger.send({label:"snappy.uncompress",error:err});
if(err) {
error(node,Error(err));
return;
}
node.setData(RED,node,msg,data,callback)
})
},
XLSXToArray:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSX2Array(data)),
XLSXObjectToArray:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSXObject2Array(data)),
XLSXToJSON:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSX2JSON(data)),
XLSXObjectToJSON:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSXObject2JSON(data)),
XLSXToXLSXObject:(RED,node,msg,data,callback)=>callback(RED,node,msg,xlsx2.XLSX2XLSXObject(data)),
XMLToJSON: (RED,node,msg,data,callback)=>callback(RED,node,msg,xmlParser.parse(data,XMLoptions,true)),
invalidArray:(v=>!Array.isArray(v))
};
function evalFunction(id,mapping){
try{
return eval(mapping);
} catch(ex) {
throw Error(id+" "+ex.message);
}
}
const is=(node,value)=>{
return node.actionSource==value||node.actionTarget==value;
}
let jsonata;
module.exports = function (RED) {
function transformNode(n) {
RED.nodes.createNode(this,n);
if(logger.active) logger.send({label:"transformNode",node:n});
let node=Object.assign(this,{maxMessages:1000,SendArray:SendArray},n,{RED:RED});
try{
if(is(node,"XLSX")||is(node,"XLSXObject") ) {
if(!XLSX) XLSX=require('xlsx');
} else if(is(node,"AVRO") || is(node,"Confluence")) {
if(avsc==null) avsc=require('avsc');
try{
node.schemaValid=eval("("+node.schema+")");
} catch(ex){
throw Error("schema "+ex.message);
}
if(!node.schemaValid) throw Error("invalid schema")
} else if(is(node,"snappy")) {
if(snappy==null) snappy=require('snappy');
} else if(is(node,"XML")) {
if(xmlParser==null) xmlParser=require('fast-xml-parser');
if(json2xmlParser==null) {
const j2xParser=xmlParser.j2xParser;
json2xmlParser=new j2xParser(XMLoptions);
}
if(logger.active) logger.send({label:"load xml",xmlParserKeys:Object.keys(xmlParser),json2xmlParser:Object.keys(json2xmlParser)});
}
if(['Append','Concat','EndsWith','Prepend','Split','StartsWith'].includes(node.actionTarget)) {
typedInput.setGetFunction(RED,node,"string")
}
const source=(node.sourceProperty||"msg.payload")
const target=(node.targetProperty||"msg.payload")
const sourceMap="(RED,node,msg)=>"+source,
deleteSource=typedInput.getValue(RED,node,"deleteSource",true)
targetMap="(RED,node,msg,data,callback)=>{"+target+"=data;"+
(node.topicProperty? "msg.topic=node.topicFunction(RED,node,msg,data);":"")+
(deleteSource&&source!==target?"delete "+source+";"+"delete "+source+";":"")+
"}",
topicMap="(RED,node,msg,data,index)=>"+(node.topicProperty||"msg.topic");
logger.sendInfo({label:"mappings",source:sourceMap,deleteSource:deleteSource,target:targetMap,topicMap:topicMap});
const getData=evalFunction("source",sourceMap);
node.getData=(RED,node,msg,callback)=>callback(RED,node,msg,getData(RED,node,msg))
const setData1=evalFunction("target",targetMap);
node.setData=(RED,node,msg,data,callback)=>{
setData1(RED,node,msg,data)
callback(RED,node,msg,data)
}
node.topicFunction=evalFunction("topic",topicMap);
if(is(node,"AVRO")) {
node.avroTransformer=avsc.Type.forSchema(node.schemaValid);
} else if(is(node,"Compressed")) {
if(compressor==null) {
const CompressionTool = require('compressiontool')
compressor=new CompressionTool();
compressor[node.compressionType]();
}
} else if(is(node,"Confluence")) {
node.schemas={};
for(const schema in node.schemaValid )
node.schemas[schema]=avsc.Type.forSchema(node.schemaValid[schema]);
logger.info({label:"confluence",schemas:Object.keys(node.schemas)});
} else if(node.actionSource=="Date") {
if(node.maxDate) node.maxDateTyped=toDateTypeZulu(node.maxDate)
if(node.minDate) node.minDateTyped=toDateTypeZulu(node.minDate)
}
} catch(ex) {
logger.error(n);
error(node,ex,"Invalid setup "+ex.message);
return;
}
if(is(node,"ISO8583")) {
if(!ISO8583) {
try{
ISO8583=require('iso-8583');
ISO8583message=ISO8583.Message();
node.log("loaded iso-8583");
} catch (ex) {
error(node,Error("need to run 'npm install iso-8583'"),"iso-8583 not installed");
return;
}
}
}
if(node.actionSource=="JSON" && node.JSONataSource){
try{
if(!jsonata) jsonata=require('jsonata')
node.JSONataSourceExpression = jsonata(node.JSONataSource)
node.getData1=node.getData
node.getData=(RED,node,msg,callback)=>
node.getData1(RED,node,msg,(RED,node,msg,data)=>
node.JSONataSourceExpression.evaluate(data,
{msg:msg,RED:RED,node:node},
(err,response)=>err?error(node,Error(err),err):callback(RED,node,msg,response))
)
} catch (ex) {
console.log("JSONata source:",node.JSONataSource)
error(node,ex,"JSONata source function error")
return
}
}
if(node.actionTarget=="JSON"&& node.JSONataTarget){
try{
if(!jsonata) jsonata=require('jsonata')
node.JSONataTargetExpression = jsonata(node.JSONataTarget)
node.setData1=node.setData
node.setData=(RED,node,msg,data,callback)=>{
node.JSONataTargetExpression.evaluate(data,{msg:msg,RED:RED,node:node},
(err,data)=>{
if(err) error(node,ex,"JSONata target evaluate error")
node.setData1(RED,node,msg,data,callback)
}
)
}
} catch (ex) {
console.log("JSONata Target:",node.JSONataTarget)
error(node,ex,"JSONata target function error")
return
}
}
const typeValidate="invalid"+node.actionSource;
node.invalidSourceType=typeValidate in functions &! ["XLSX","XLSXObject"].includes(node.actionTarget)?functions[typeValidate]:(()=>false);
try {
node.transform=functions[node.actionSource+"To"+node.actionTarget];
if(!node.transform) throw Error("transform routine not found for "+node.actionSource+" to "+node.actionTarget);
} catch (ex) {
error(node,ex,node.actionSource+"\nto "+node.actionTarget + " not implemented")
return;
}
// node.processMsg=node.sendInFunction?this.transform
// :(RED,node,msg,data,callback)=>node.setData(RED,node,msg,node.transform(RED,node,msg,data),callback);
this.status({fill:"green",shape:"ring",text:"Ready"});
node.on("input", function(msg) {
if(logger.active) logger.send({label:"input",msgid:msg._msgid,topic:msg.topic});
try{
node.getData(RED,node,msg,(RED,node,msg,data)=>{
if(node.invalidSourceType(data)) {
msg.error=node.actionSource+" to "+node.actionTarget + " expected source data type "+node.actionSource;
error(node,Error(msg.error),"Error(s)");
node.send([null,msg]);
}
node.transform(RED,node,msg,data,(RED,node,msg,dataTransformed)=>node.setData(RED,node,msg,dataTransformed,()=>node.send([msg]) ))
})
} catch (ex) {
logger.sendErrorAndDump("on input error",ex)
msg.error=node.actionSource+" to "+node.actionTarget + " " +ex.message;
error(node,Error(msg.error),"Error(s)");
node.send([null,msg]);
}
});
}
RED.nodes.registerType(logger.label,transformNode);
};