const parseTopTracks = topTracks => { const expression = jsonata(topTracksPath); expression.registerFunction('getProfileUrl', (artist, track) => { const { url } = routes.get_korin_profiles({ artist, track }); return url; }); const tracks = expression.evaluate(topTracks); return tracks; };
function prepareJSONataExpression(value,node) { var expr = jsonata(value); expr.assign('flowContext',function(val) { return node.context().flow.get(val); }); expr.assign('globalContext',function(val) { return node.context().global.get(val); }); expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value); return expr; }
/** * Prepares a JSONata expression for evaluation. * This attaches Node-RED specific functions to the expression. * * @param {String} value - the JSONata expression * @param {Node} node - the node evaluating the property * @return {Object} The JSONata expression that can be evaluated * @memberof @node-red/util_util */ function prepareJSONataExpression(value,node) { var expr = jsonata(value); expr.assign('flowContext',function(val) { return node.context().flow.get(val); }); expr.assign('globalContext',function(val) { return node.context().global.get(val); }); expr.assign('env', function(name) { var val = getSetting(node, name); return (val ? val : ""); }) expr.registerFunction('clone', cloneMessage, '<(oa)-:o>'); expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value); expr._node = node; return expr; }
/** * Query all of the specified resources using the given JSONata expression. * @param {string} expression The JSONata expression. * @param {Resource[]} resources The resource to query. * @return {Promise} A promise that will be resolved with the results of the * query, or rejected with an error. */ queryAll(expression, resources) { const method = 'queryAll'; LOG.entry(method, expression, resources); // Compile the expression. LOG.debug(method, 'Compiling JSONata expression'); const compiledExpression = jsonata(expression); LOG.debug(method, 'Compiled JSONata expression'); // Prepare the root resources. let cachedResources = new Map(); resources.forEach((resource) => { LOG.debug(method, 'Preparing resource', resource.getFullyQualifiedIdentifier()); let fqi = resource.getFullyQualifiedIdentifier(); cachedResources.set(fqi, resource); this.prepareResource(resource); }); // Process the query by calling the method that does the bulk of the work. let promise = Promise.resolve(); let result = []; resources.forEach((resource) => { promise = promise.then(() => { LOG.debug(method, 'Executing query on resource', resource.getFullyQualifiedIdentifier()); return this.queryInternal(compiledExpression, resource, cachedResources) .then((thisResult) => { LOG.debug(method, 'Executed query, adding result to list'); result.push(thisResult); }); }); }); return promise .then(() => { LOG.exit(method, result); return result; }); }
/** * Query the specified resource using the given JSONata expression. * @param {string} expression The JSONata expression. * @param {Resource} resource The resource to query. * @return {Promise} A promise that will be resolved with the results of the * query, or rejected with an error. */ query(expression, resource) { const method = 'query'; LOG.entry(method, expression, resource.toString()); // Compile the expression. LOG.debug(method, 'Compiling JSONata expression'); const compiledExpression = jsonata(expression); LOG.debug(method, 'Compiled JSONata expression'); // Prepare the root resource. let cachedResources = new Map(); let fqi = resource.getFullyQualifiedIdentifier(); cachedResources.set(fqi, resource); this.prepareResource(resource); // Process the query by calling the method that does the bulk of the work. return this.queryInternal(compiledExpression, resource, cachedResources) .then((result) => { LOG.exit(method, result); return result; }); }
function SwitchNode(n) { RED.nodes.createNode(this, n); this.rules = n.rules || []; this.property = n.property; this.propertyType = n.propertyType || "msg"; if (this.propertyType === 'jsonata') { try { this.property = jsonata(this.property); } catch(err) { this.error(RED._("switch.errors.invalid-expr",{error:err.message})); return; } } this.checkall = n.checkall || "true"; this.previousValue = null; var node = this; var valid = true; for (var i=0; i<this.rules.length; i+=1) { var rule = this.rules[i]; if (!rule.vt) { if (!isNaN(Number(rule.v))) { rule.vt = 'num'; } else { rule.vt = 'str'; } } if (rule.vt === 'num') { if (!isNaN(Number(rule.v))) { rule.v = Number(rule.v); } } else if (rule.vt === "jsonata") { try { rule.v = jsonata(rule.v); } catch(err) { this.error(RED._("switch.errors.invalid-expr",{error:err.message})); valid = false; } } if (typeof rule.v2 !== 'undefined') { if (!rule.v2t) { if (!isNaN(Number(rule.v2))) { rule.v2t = 'num'; } else { rule.v2t = 'str'; } } if (rule.v2t === 'num') { rule.v2 = Number(rule.v2); } else if (rule.v2t === 'jsonata') { try { rule.v2 = jsonata(rule.v2); } catch(err) { this.error(RED._("switch.errors.invalid-expr",{error:err.message})); valid = false; } } } } if (!valid) { return; } this.on('input', function (msg) { var onward = []; try { var prop; if (node.propertyType === 'jsonata') { prop = node.property.evaluate({msg:msg}); } else { prop = RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg); } var elseflag = true; for (var i=0; i<node.rules.length; i+=1) { var rule = node.rules[i]; var test = prop; var v1,v2; if (rule.vt === 'prev') { v1 = node.previousValue; } else if (rule.vt === 'jsonata') { try { v1 = rule.v.evaluate({msg:msg}); } catch(err) { node.error(RED._("switch.errors.invalid-expr",{error:err.message})); return; } } else { try { v1 = RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg); } catch(err) { v1 = undefined; } } v2 = rule.v2; if (rule.v2t === 'prev') { v2 = node.previousValue; } else if (rule.v2t === 'jsonata') { try { v2 = rule.v2.evaluate({msg:msg}); } catch(err) { node.error(RED._("switch.errors.invalid-expr",{error:err.message})); return; } } else if (typeof v2 !== 'undefined') { try { v2 = RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg); } catch(err) { v2 = undefined; } } if (rule.t == "else") { test = elseflag; elseflag = true; } if (operators[rule.t](test,v1,v2,rule.case)) { onward.push(msg); elseflag = false; if (node.checkall == "false") { break; } } else { onward.push(null); } } node.previousValue = prop; this.send(onward); } catch(err) { node.warn(err); } }); }
const getSongId = data => { const expression = jsonata(lyricsIdPath); const songId = expression.evaluate(data); return songId; };
function ChangeNode(n) { RED.nodes.createNode(this, n); this.rules = n.rules; var rule; if (!this.rules) { rule = { t:(n.action=="replace"?"set":n.action), p:n.property||"" } if ((rule.t === "set")||(rule.t === "move")) { rule.to = n.to||""; } else if (rule.t === "change") { rule.from = n.from||""; rule.to = n.to||""; rule.re = (n.reg===null||n.reg); } this.rules = [rule]; } var valid = true; for (var i=0;i<this.rules.length;i++) { rule = this.rules[i]; // Migrate to type-aware rules if (!rule.pt) { rule.pt = "msg"; } if (rule.t === "change" && rule.re) { rule.fromt = 're'; delete rule.re; } if (rule.t === "set" && !rule.tot) { if (rule.to.indexOf("msg.") === 0 && !rule.tot) { rule.to = rule.to.substring(4); rule.tot = "msg"; } } if (!rule.tot) { rule.tot = "str"; } if (!rule.fromt) { rule.fromt = "str"; } if (rule.t === "change" && rule.fromt !== 'msg' && rule.fromt !== 'flow' && rule.fromt !== 'global') { rule.fromRE = rule.from; if (rule.fromt !== 're') { rule.fromRE = rule.fromRE.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); } try { rule.fromRE = new RegExp(rule.fromRE, "g"); } catch (e) { valid = false; this.error(RED._("change.errors.invalid-from",{error:e.message})); } } if (rule.tot === 'num') { rule.to = Number(rule.to); } else if (rule.tot === 'json') { try { // check this is parsable JSON JSON.parse(rule.to); } catch(e2) { valid = false; this.error(RED._("change.errors.invalid-json")); } } else if (rule.tot === 'bool') { rule.to = /^true$/i.test(rule.to); } else if (rule.tot === 'jsonata') { try { rule.to = jsonata(rule.to); } catch(e) { valid = false; this.error(RED._("change.errors.invalid-from",{error:e.message})); } } } function applyRule(msg,rule) { try { var property = rule.p; var value = rule.to; if (rule.tot === 'json') { value = JSON.parse(rule.to); } var current; var fromValue; var fromType; var fromRE; if (rule.tot === "msg") { value = RED.util.getMessageProperty(msg,rule.to); } else if (rule.tot === 'flow') { value = node.context().flow.get(rule.to); } else if (rule.tot === 'global') { value = node.context().global.get(rule.to); } else if (rule.tot === 'date') { value = Date.now(); } else if (rule.tot === 'jsonata') { value = rule.to.evaluate({msg:msg}); } if (rule.t === 'change') { if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') { if (rule.fromt === "msg") { fromValue = RED.util.getMessageProperty(msg,rule.from); } else if (rule.tot === 'flow') { fromValue = node.context().flow.get(rule.from); } else if (rule.tot === 'global') { fromValue = node.context().global.get(rule.from); } if (typeof fromValue === 'number' || fromValue instanceof Number) { fromType = 'num'; } else if (typeof fromValue === 'boolean') { fromType = 'bool' } else if (fromValue instanceof RegExp) { fromType = 're'; fromRE = fromValue; } else if (typeof fromValue === 'string') { fromType = 'str'; fromRE = fromValue.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); try { fromRE = new RegExp(fromRE, "g"); } catch (e) { valid = false; node.error(RED._("change.errors.invalid-from",{error:e.message})); return; } } else { node.error(RED._("change.errors.invalid-from",{error:"unsupported type: "+(typeof fromValue)})); return } } else { fromType = rule.fromt; fromValue = rule.from; fromRE = rule.fromRE; } } if (rule.pt === 'msg') { if (rule.t === 'delete') { RED.util.setMessageProperty(msg,property,undefined); } else if (rule.t === 'set') { RED.util.setMessageProperty(msg,property,value); } else if (rule.t === 'change') { current = RED.util.getMessageProperty(msg,property); if (typeof current === 'string') { if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) { // str representation of exact from number/boolean // only replace if they match exactly RED.util.setMessageProperty(msg,property,value); } else { current = current.replace(fromRE,value); RED.util.setMessageProperty(msg,property,current); } } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') { if (current == Number(fromValue)) { RED.util.setMessageProperty(msg,property,value); } } else if (typeof current === 'boolean' && fromType === 'bool') { if (current.toString() === fromValue) { RED.util.setMessageProperty(msg,property,value); } } } } else { var target; if (rule.pt === 'flow') { target = node.context().flow; } else if (rule.pt === 'global') { target = node.context().global; } if (target) { if (rule.t === 'delete') { target.set(property,undefined); } else if (rule.t === 'set') { target.set(property,value); } else if (rule.t === 'change') { current = target.get(msg,property); if (typeof current === 'string') { if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) { // str representation of exact from number/boolean // only replace if they match exactly target.set(property,value); } else { current = current.replace(fromRE,value); target.set(property,current); } } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') { if (current == Number(fromValue)) { target.set(property,value); } } else if (typeof current === 'boolean' && fromType === 'bool') { if (current.toString() === fromValue) { target.set(property,value); } } } } } } catch(err) {/*console.log(err.stack)*/} return msg; } if (valid) { var node = this; this.on('input', function(msg) { for (var i=0; i<this.rules.length; i++) { if (this.rules[i].t === "move") { var r = this.rules[i]; if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) { msg = applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt}); applyRule(msg,{t:"delete", p:r.p, pt:r.pt}); } else { // 2 step move if we are moving from a child msg = applyRule(msg,{t:"set", p:"_temp_move", pt:r.tot, to:r.p, tot:r.pt}); applyRule(msg,{t:"delete", p:r.p, pt:r.pt}); msg = applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:"_temp_move", tot:r.pt}); applyRule(msg,{t:"delete", p:"_temp_move", pt:r.pt}); } } else { msg = applyRule(msg,this.rules[i]); } if (msg === null) { return; } } node.send(msg); }); } }