Merge pull request #4590 from node-red/jsonata-update

Upgrade to JSONata 2.x
make-split/join-more-flexible^2
Nick O'Leary 2024-03-07 14:28:35 +00:00 committed by GitHub
commit e4dc1779c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 96 additions and 32 deletions

View File

@ -54,7 +54,7 @@
"is-utf8": "0.2.1", "is-utf8": "0.2.1",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"jsonata": "1.8.6", "jsonata": "2.0.4",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0", "media-typer": "1.1.0",
"memorystore": "1.6.7", "memorystore": "1.6.7",

View File

@ -777,12 +777,15 @@ function evaluateJSONataExpression(expr,msg,callback) {
}); });
} }
} else { } else {
log.warn('Deprecated API warning: Calls to RED.util.evaluateJSONataExpression must include a callback. '+ const error = new Error('Calls to RED.util.evaluateJSONataExpression must include a callback.')
'This will not be optional in Node-RED 4.0. Please identify the node from the following stack '+ throw error
'and check for an update on npm. If none is available, please notify the node author.')
log.warn(new Error().stack)
} }
return expr.evaluate(context, bindings, callback);
expr.evaluate(context, bindings).then(result => {
callback(null, result)
}).catch(err => {
callback(err)
})
} }
/** /**

View File

@ -18,7 +18,7 @@
"fs-extra": "11.1.1", "fs-extra": "11.1.1",
"i18next": "21.10.0", "i18next": "21.10.0",
"json-stringify-safe": "5.0.1", "json-stringify-safe": "5.0.1",
"jsonata": "1.8.6", "jsonata": "2.0.4",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"moment": "2.29.4", "moment": "2.29.4",
"moment-timezone": "0.5.43" "moment-timezone": "0.5.43"

View File

@ -448,9 +448,16 @@ describe("@node-red/util/util", function() {
},{}); },{});
result.should.eql("123"); result.should.eql("123");
}); });
it('returns jsonata result', function () { it('returns jsonata result', function (done) {
var result = util.evaluateNodeProperty('$abs(-1)','jsonata',{},{}); util.evaluateNodeProperty('$abs(-1)','jsonata',{},{}, (err, result) => {
result.should.eql(1); try {
result.should.eql(1);
done()
} catch (error) {
done(error)
}
});
}); });
it('returns null', function() { it('returns null', function() {
var result = util.evaluateNodeProperty(null,'null'); var result = util.evaluateNodeProperty(null,'null');
@ -608,51 +615,105 @@ describe("@node-red/util/util", function() {
}); });
}); });
describe('evaluateJSONataExpression', function() { describe('evaluateJSONataExpression', function() {
it('evaluates an expression', function() { it('evaluates an expression', function(done) {
var expr = util.prepareJSONataExpression('payload',{}); var expr = util.prepareJSONataExpression('payload',{});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); util.evaluateJSONataExpression(expr,{payload:"hello"}, (err, result) => {
result.should.eql("hello"); try {
result.should.eql("hello");
done()
} catch (error) {
done(error)
}
});
}); });
it('evaluates a legacyMode expression', function() { it('evaluates a legacyMode expression', function() {
var expr = util.prepareJSONataExpression('msg.payload',{}); var expr = util.prepareJSONataExpression('msg.payload',{});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); util.evaluateJSONataExpression(expr,{payload:"hello"}, (err, result) => {
result.should.eql("hello"); try {
result.should.eql("hello");
done()
} catch (error) {
done(error)
}
});
}); });
it('accesses flow context from an expression', function() { it('accesses flow context from an expression', function() {
var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}}); var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); util.evaluateJSONataExpression(expr,{payload:"hello"}, (err, result) => {
result.should.eql("bar"); try {
result.should.eql("bar");
done()
} catch (error) {
done(error)
}
});
}); });
it('accesses undefined environment variable from an expression', function() { it('accesses undefined environment variable from an expression', function() {
var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{}); var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{});
var result = util.evaluateJSONataExpression(expr,{}); util.evaluateJSONataExpression(expr,{}, (err, result) => {
result.should.eql(''); try {
}); result.should.eql("");
done()
} catch (error) {
done(error)
}
});
});
it('accesses environment variable from an expression', function() { it('accesses environment variable from an expression', function() {
process.env.UTIL_ENV = 'foo'; process.env.UTIL_ENV = 'foo';
var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{}); var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{});
var result = util.evaluateJSONataExpression(expr,{}); util.evaluateJSONataExpression(expr,{}, (err, result) => {
result.should.eql('foo'); try {
}); result.should.eql("foo");
done()
} catch (error) {
done(error)
}
});
});
it('accesses moment from an expression', function() { it('accesses moment from an expression', function() {
var expr = util.prepareJSONataExpression('$moment("2020-05-27", "YYYY-MM-DD").add(7, "days").add(1, "months").format("YYYY-MM-DD")',{}); var expr = util.prepareJSONataExpression('$moment("2020-05-27", "YYYY-MM-DD").add(7, "days").add(1, "months").format("YYYY-MM-DD")',{});
var result = util.evaluateJSONataExpression(expr,{}); util.evaluateJSONataExpression(expr,{}, (err, result) => {
result.should.eql('2020-07-03'); try {
result.should.eql("2020-07-03");
done()
} catch (error) {
done(error)
}
});
}); });
it('accesses moment-timezone from an expression', function() { it('accesses moment-timezone from an expression', function() {
var expr = util.prepareJSONataExpression('$moment("2013-11-18 11:55Z").tz("Asia/Taipei").format()',{}); var expr = util.prepareJSONataExpression('$moment("2013-11-18 11:55Z").tz("Asia/Taipei").format()',{});
var result = util.evaluateJSONataExpression(expr,{}); util.evaluateJSONataExpression(expr,{}, (err, result) => {
result.should.eql('2013-11-18T19:55:00+08:00'); try {
result.should.eql("2013-11-18T19:55:00+08:00");
done()
} catch (error) {
done(error)
}
});
}); });
it('handles non-existant flow context variable', function() { it('handles non-existant flow context variable', function() {
var expr = util.prepareJSONataExpression('$flowContext("nonExistant")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}}); var expr = util.prepareJSONataExpression('$flowContext("nonExistant")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); util.evaluateJSONataExpression(expr,{payload:"hello"}, (err, result) => {
should.not.exist(result); try {
}); should.not.exist(result);
done()
} catch (error) {
done(error)
}
});
});
it('handles non-existant global context variable', function() { it('handles non-existant global context variable', function() {
var expr = util.prepareJSONataExpression('$globalContext("nonExistant")',{context:function() { return {global:{get: function(key) { return {'foo':'bar'}[key]}}}}}); var expr = util.prepareJSONataExpression('$globalContext("nonExistant")',{context:function() { return {global:{get: function(key) { return {'foo':'bar'}[key]}}}}});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); util.evaluateJSONataExpression(expr,{payload:"hello"}, (err, result) => {
should.not.exist(result); try {
should.not.exist(result);
done()
} catch (error) {
done(error)
}
});
}); });
it('handles async flow context access', function(done) { it('handles async flow context access', function(done) {
var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key,store,callback) { setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key,store,callback) { setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}});