mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add JSONata expr tester and improved feedback
This commit is contained in:
parent
b030e935ce
commit
dbf0486acb
@ -124,6 +124,7 @@ module.exports = function(grunt) {
|
|||||||
"editor/js/ui/utils.js",
|
"editor/js/ui/utils.js",
|
||||||
"editor/js/ui/common/editableList.js",
|
"editor/js/ui/common/editableList.js",
|
||||||
"editor/js/ui/common/menu.js",
|
"editor/js/ui/common/menu.js",
|
||||||
|
"editor/js/ui/common/panels.js",
|
||||||
"editor/js/ui/common/popover.js",
|
"editor/js/ui/common/popover.js",
|
||||||
"editor/js/ui/common/searchBox.js",
|
"editor/js/ui/common/searchBox.js",
|
||||||
"editor/js/ui/common/tabs.js",
|
"editor/js/ui/common/tabs.js",
|
||||||
|
83
editor/js/ui/common/panels.js
Normal file
83
editor/js/ui/common/panels.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
RED.panels = (function() {
|
||||||
|
|
||||||
|
function createPanel(options) {
|
||||||
|
var container = options.container || $("#"+options.id);
|
||||||
|
var children = container.children();
|
||||||
|
if (children.length !== 2) {
|
||||||
|
throw new Error("Container must have exactly two children");
|
||||||
|
}
|
||||||
|
|
||||||
|
container.addClass("red-ui-panels");
|
||||||
|
var separator = $('<div class="red-ui-panels-separator"></div>').insertAfter(children[0]);
|
||||||
|
var startPosition;
|
||||||
|
var panelHeights = [];
|
||||||
|
var modifiedHeights = false;
|
||||||
|
var panelRatio;
|
||||||
|
|
||||||
|
separator.draggable({
|
||||||
|
axis: "y",
|
||||||
|
containment: container,
|
||||||
|
scroll: false,
|
||||||
|
start:function(event,ui) {
|
||||||
|
var height = container.height();
|
||||||
|
startPosition = ui.position.top;
|
||||||
|
panelHeights = [$(children[0]).height(),$(children[1]).height()];
|
||||||
|
console.log("START",height,panelHeights,panelHeights[0]+panelHeights[1],height-(panelHeights[0]+panelHeights[1]));
|
||||||
|
},
|
||||||
|
drag: function(event,ui) {
|
||||||
|
var height = container.height();
|
||||||
|
var delta = ui.position.top-startPosition;
|
||||||
|
var newHeights = [panelHeights[0]+delta,panelHeights[1]-delta];
|
||||||
|
$(children[0]).height(newHeights[0]);
|
||||||
|
$(children[1]).height(newHeights[1]);
|
||||||
|
if (options.resize) {
|
||||||
|
options.resize(newHeights[0],newHeights[1]);
|
||||||
|
}
|
||||||
|
ui.position.top -= delta;
|
||||||
|
panelRatio = newHeights[0]/height;
|
||||||
|
},
|
||||||
|
stop:function(event,ui) {
|
||||||
|
modifiedHeights = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
resize: function(height) {
|
||||||
|
var panelHeights = [$(children[0]).height(),$(children[1]).height()];
|
||||||
|
container.height(height);
|
||||||
|
if (modifiedHeights) {
|
||||||
|
var topPanelHeight = panelRatio*height;
|
||||||
|
var bottomPanelHeight = height - topPanelHeight - 48;
|
||||||
|
panelHeights = [topPanelHeight,bottomPanelHeight];
|
||||||
|
$(children[0]).height(panelHeights[0]);
|
||||||
|
$(children[1]).height(panelHeights[1]);
|
||||||
|
console.log("SET",height,panelHeights,panelHeights[0]+panelHeights[1],height-(panelHeights[0]+panelHeights[1]));
|
||||||
|
}
|
||||||
|
if (options.resize) {
|
||||||
|
options.resize(panelHeights[0],panelHeights[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
create: createPanel
|
||||||
|
}
|
||||||
|
})();
|
@ -1598,6 +1598,9 @@ RED.editor = (function() {
|
|||||||
editStack.push({type:type});
|
editStack.push({type:type});
|
||||||
RED.view.state(RED.state.EDITING);
|
RED.view.state(RED.state.EDITING);
|
||||||
var expressionEditor;
|
var expressionEditor;
|
||||||
|
var testDataEditor;
|
||||||
|
var testResultEditor
|
||||||
|
var panels;
|
||||||
|
|
||||||
var trayOptions = {
|
var trayOptions = {
|
||||||
title: getEditStackTitle(),
|
title: getEditStackTitle(),
|
||||||
@ -1621,20 +1624,18 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
resize: function(dimensions) {
|
resize: function(dimensions) {
|
||||||
editTrayWidthCache[type] = dimensions.width;
|
if (dimensions) {
|
||||||
|
editTrayWidthCache[type] = dimensions.width;
|
||||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
|
||||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
|
||||||
var height = $("#dialog-form").height();
|
|
||||||
for (var i=0;i<rows.size();i++) {
|
|
||||||
height -= $(rows[i]).outerHeight(true);
|
|
||||||
}
|
}
|
||||||
height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
|
var height = $("#dialog-form").height();
|
||||||
$(".node-text-editor").css("height",height+"px");
|
if (panels) {
|
||||||
expressionEditor.resize();
|
panels.resize(height);
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
open: function(tray) {
|
open: function(tray) {
|
||||||
var trayBody = tray.find('.editor-tray-body');
|
var trayBody = tray.find('.editor-tray-body');
|
||||||
|
trayBody.addClass("node-input-expression-editor")
|
||||||
var dialogForm = buildEditForm(tray.find('.editor-tray-body'),'dialog-form','_expression','editor');
|
var dialogForm = buildEditForm(tray.find('.editor-tray-body'),'dialog-form','_expression','editor');
|
||||||
var funcSelect = $("#node-input-expression-func");
|
var funcSelect = $("#node-input-expression-func");
|
||||||
Object.keys(jsonata.functions).forEach(function(f) {
|
Object.keys(jsonata.functions).forEach(function(f) {
|
||||||
@ -1749,6 +1750,118 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
expressionEditor.getSession().setValue(v||"",-1);
|
expressionEditor.getSession().setValue(v||"",-1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var tabs = RED.tabs.create({
|
||||||
|
element: $("#node-input-expression-tabs"),
|
||||||
|
onchange:function(tab) {
|
||||||
|
$(".node-input-expression-tab-content").hide();
|
||||||
|
tab.content.show();
|
||||||
|
trayOptions.resize();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
tabs.addTab({
|
||||||
|
id: 'expression-help',
|
||||||
|
label: 'Function reference',
|
||||||
|
content: $("#node-input-expression-tab-help")
|
||||||
|
});
|
||||||
|
tabs.addTab({
|
||||||
|
id: 'expression-tests',
|
||||||
|
label: 'Test',
|
||||||
|
content: $("#node-input-expression-tab-test")
|
||||||
|
});
|
||||||
|
testDataEditor = RED.editor.createEditor({
|
||||||
|
id: 'node-input-expression-test-data',
|
||||||
|
value: '{\n "payload": "hello world"\n}',
|
||||||
|
mode:"ace/mode/json",
|
||||||
|
lineNumbers: false
|
||||||
|
});
|
||||||
|
var changeTimer;
|
||||||
|
$(".node-input-expression-legacy").click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
RED.sidebar.info.set(RED._("expressionEditor.compatModeDesc"));
|
||||||
|
RED.sidebar.info.show();
|
||||||
|
})
|
||||||
|
var testExpression = function() {
|
||||||
|
var value = testDataEditor.getValue();
|
||||||
|
var parsedData;
|
||||||
|
var currentExpression = expressionEditor.getValue();
|
||||||
|
var expr;
|
||||||
|
var usesContext = false;
|
||||||
|
var legacyMode = false;
|
||||||
|
try {
|
||||||
|
expr = jsonata(currentExpression);
|
||||||
|
expr.assign('flow',function(val) {
|
||||||
|
usesContext = true;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
expr.assign('global',function(val) {
|
||||||
|
usesContext = true;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
|
||||||
|
} catch(err) {
|
||||||
|
testResultEditor.setValue("Invalid jsonata expression:\n "+err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$(".node-input-expression-legacy").toggle(legacyMode);
|
||||||
|
try {
|
||||||
|
parsedData = JSON.parse(value);
|
||||||
|
} catch(err) {
|
||||||
|
testResultEditor.setValue("Invalid example message:\n "+err.toString())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var result = expr.evaluate(legacyMode?{msg:parsedData}:parsedData);
|
||||||
|
if (usesContext) {
|
||||||
|
testResultEditor.setValue("Cannot test a function that uses context values");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var formattedResult = JSON.stringify(result,null,4);
|
||||||
|
testResultEditor.setValue(formattedResult || "No match");
|
||||||
|
} catch(err) {
|
||||||
|
testResultEditor.setValue("Error evaluating expression:\n "+err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testDataEditor.getSession().on('change', function() {
|
||||||
|
clearTimeout(changeTimer);
|
||||||
|
changeTimer = setTimeout(testExpression,200);
|
||||||
|
});
|
||||||
|
expressionEditor.getSession().on('change', function() {
|
||||||
|
clearTimeout(changeTimer);
|
||||||
|
changeTimer = setTimeout(testExpression,200);
|
||||||
|
});
|
||||||
|
|
||||||
|
testResultEditor = RED.editor.createEditor({
|
||||||
|
id: 'node-input-expression-test-result',
|
||||||
|
value: "",
|
||||||
|
mode:"ace/mode/json",
|
||||||
|
lineNumbers: false,
|
||||||
|
readOnly: true
|
||||||
|
});
|
||||||
|
panels = RED.panels.create({
|
||||||
|
id:"node-input-expression-panels",
|
||||||
|
resize: function(p1Height,p2Height) {
|
||||||
|
var p1 = $("#node-input-expression-panel-expr");
|
||||||
|
p1Height -= $(p1.children()[0]).outerHeight(true);
|
||||||
|
var editorRow = $(p1.children()[1]);
|
||||||
|
p1Height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||||
|
$("#node-input-expression").css("height",(p1Height-5)+"px");
|
||||||
|
expressionEditor.resize();
|
||||||
|
|
||||||
|
var p2 = $("#node-input-expression-panel-info > .form-row > div:first-child");
|
||||||
|
p2Height -= p2.outerHeight(true) + 20;
|
||||||
|
$(".node-input-expression-tab-content").height(p2Height);
|
||||||
|
$("#node-input-expression-test-data").css("height",(p2Height-5)+"px");
|
||||||
|
testDataEditor.resize();
|
||||||
|
$("#node-input-expression-test-result").css("height",(p2Height-5)+"px");
|
||||||
|
testResultEditor.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testExpression();
|
||||||
},
|
},
|
||||||
close: function() {
|
close: function() {
|
||||||
editStack.pop();
|
editStack.pop();
|
||||||
@ -1855,6 +1968,7 @@ RED.editor = (function() {
|
|||||||
validateNode: validateNode,
|
validateNode: validateNode,
|
||||||
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
||||||
|
|
||||||
|
|
||||||
createEditor: function(options) {
|
createEditor: function(options) {
|
||||||
var editor = ace.edit(options.id);
|
var editor = ace.edit(options.id);
|
||||||
editor.setTheme("ace/theme/tomorrow");
|
editor.setTheme("ace/theme/tomorrow");
|
||||||
@ -1875,6 +1989,12 @@ RED.editor = (function() {
|
|||||||
enableSnippets:true
|
enableSnippets:true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (options.readOnly) {
|
||||||
|
editor.setOption('readOnly',options.readOnly);
|
||||||
|
}
|
||||||
|
if (options.hasOwnProperty('lineNumbers')) {
|
||||||
|
editor.renderer.setOption('showGutter',options.lineNumbers);
|
||||||
|
}
|
||||||
editor.$blockScrolling = Infinity;
|
editor.$blockScrolling = Infinity;
|
||||||
if (options.value) {
|
if (options.value) {
|
||||||
session.setValue(options.value,-1);
|
session.setValue(options.value,-1);
|
||||||
|
@ -350,7 +350,8 @@ RED.sidebar.info = (function() {
|
|||||||
// tips.stop();
|
// tips.stop();
|
||||||
sections.show();
|
sections.show();
|
||||||
nodeSection.container.hide();
|
nodeSection.container.hide();
|
||||||
$(infoSection.content).html(html);
|
var wrapped = $('<div class="node-help"></div>').html(html);
|
||||||
|
$(infoSection.content).empty().append(wrapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,8 +48,10 @@
|
|||||||
.editor-tray-body {
|
.editor-tray-body {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
form {
|
padding: 0.1px; // prevent margin collapsing
|
||||||
|
.dialog-form,#dialog-form, #dialog-config-form {
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
height: calc(100% - 40px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.editor-tray-content {
|
.editor-tray-content {
|
||||||
@ -154,7 +156,7 @@
|
|||||||
|
|
||||||
|
|
||||||
.dialog-form,#dialog-form, #dialog-config-form {
|
.dialog-form,#dialog-form, #dialog-config-form {
|
||||||
height: calc(100% - 20px);
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-error {
|
.input-error {
|
||||||
@ -245,3 +247,48 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
}
|
}
|
||||||
|
.node-input-expression-editor #dialog-form {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
.red-ui-panel {
|
||||||
|
&:first-child {
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.node-input-expression-tab-content {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#node-input-expression-help {
|
||||||
|
position: absolute;
|
||||||
|
top: 35px;
|
||||||
|
left:0;
|
||||||
|
right: 0;
|
||||||
|
bottom:0;
|
||||||
|
padding: 0 20px;
|
||||||
|
overflow: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
#node-input-expression-panel-info {
|
||||||
|
& > .form-row {
|
||||||
|
margin: 0;
|
||||||
|
& > div:first-child {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.node-input-expression-legacy {
|
||||||
|
float: left;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid white;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
&:hover {
|
||||||
|
border-color: $primary-border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
23
editor/sass/panels.scss
Normal file
23
editor/sass/panels.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.red-ui-panels {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
// border: 1px solid red;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.red-ui-panels-separator {
|
||||||
|
border-top: 1px solid $secondary-border-color;
|
||||||
|
border-bottom: 1px solid $secondary-border-color;
|
||||||
|
height: 7px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: ns-resize;
|
||||||
|
background: $background-color url(images/grip.png) no-repeat 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.red-ui-panel {
|
||||||
|
overflow: auto;
|
||||||
|
height: calc(50% - 4px);
|
||||||
|
}
|
@ -37,6 +37,7 @@
|
|||||||
@import "library";
|
@import "library";
|
||||||
@import "search";
|
@import "search";
|
||||||
|
|
||||||
|
@import "panels";
|
||||||
@import "tabs";
|
@import "tabs";
|
||||||
@import "tab-config";
|
@import "tab-config";
|
||||||
@import "tab-info";
|
@import "tab-info";
|
||||||
|
@ -166,22 +166,41 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/x-red" data-template-name="_expression">
|
<script type="text/x-red" data-template-name="_expression">
|
||||||
<div class="form-row" style="margin-bottom: 3px; text-align: right;">
|
<div id="node-input-expression-panels">
|
||||||
<button id="node-input-expression-reformat" class="editor-button editor-button-small">format</button>
|
<div id="node-input-expression-panel-expr" class="red-ui-panel">
|
||||||
</div>
|
<div class="form-row" style="margin-bottom: 3px; text-align: right;">
|
||||||
<div class="form-row node-text-editor-row">
|
<span class="node-input-expression-legacy"><i class="fa fa-exclamation-circle"></i> <span data-i18n="expressionEditor.compatMode"></span></span>
|
||||||
<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-expression"></div>
|
<button id="node-input-expression-reformat" class="editor-button editor-button-small"><span data-i18n="expressionEditor.format"></span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row node-text-editor-row">
|
||||||
<label for="node-input-expression-func" data-i18n="expressionEditor.functions"></label>
|
<div class="node-text-editor" id="node-input-expression"></div>
|
||||||
<select id="node-input-expression-func"></select>
|
</div>
|
||||||
<button id="node-input-expression-func-insert" class="editor-button" data-i18n="expressionEditor.insert"></button>
|
</div>
|
||||||
<div style="min-height: 200px;" id="node-input-expression-help"></div>
|
<div id="node-input-expression-panel-info" class="red-ui-panel">
|
||||||
|
<div class="form-row">
|
||||||
|
<ul id="node-input-expression-tabs"></ul>
|
||||||
|
<div id="node-input-expression-tab-help" class="node-input-expression-tab-content hide">
|
||||||
|
<div>
|
||||||
|
<select id="node-input-expression-func"></select>
|
||||||
|
<button id="node-input-expression-func-insert" class="editor-button" data-i18n="expressionEditor.insert"></button>
|
||||||
|
</div>
|
||||||
|
<div id="node-input-expression-help"></div>
|
||||||
|
</div>
|
||||||
|
<div id="node-input-expression-tab-test" class="node-input-expression-tab-content hide">
|
||||||
|
<div>
|
||||||
|
<span style="display: inline-block; width: calc(50% - 5px);" data-i18n="expressionEditor.data"></span>
|
||||||
|
<span style="display: inline-block; width: calc(50% - 5px);" data-i18n="expressionEditor.result"></span>
|
||||||
|
</div>
|
||||||
|
<div style="display: inline-block; width: calc(50% - 5px);" class="node-text-editor" id="node-input-expression-test-data"></div>
|
||||||
|
<div style="display: inline-block; width: calc(50% - 5px);" class="node-text-editor" id="node-input-expression-test-result"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
<script type="text/x-red" data-template-name="_json">
|
<script type="text/x-red" data-template-name="_json">
|
||||||
<div class="form-row" style="margin-bottom: 3px; text-align: right;">
|
<div class="form-row" style="margin-bottom: 3px; text-align: right;">
|
||||||
<button id="node-input-expression-reformat" class="editor-button editor-button-small">format</button>
|
<button id="node-input-expression-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row node-text-editor-row">
|
<div class="form-row node-text-editor-row">
|
||||||
<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>
|
<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>
|
||||||
|
5
editor/vendor/jsonata/formatter.js
vendored
5
editor/vendor/jsonata/formatter.js
vendored
@ -111,11 +111,10 @@
|
|||||||
'$average':{ args:['value'] },
|
'$average':{ args:['value'] },
|
||||||
'$boolean':{ args:['value'] },
|
'$boolean':{ args:['value'] },
|
||||||
'$contains':{ args:['str','pattern']},
|
'$contains':{ args:['str','pattern']},
|
||||||
'$context': {args:['string']},
|
|
||||||
'$count':{ args:['array'] },
|
'$count':{ args:['array'] },
|
||||||
'$exists':{ args:['value'] },
|
'$exists':{ args:['value'] },
|
||||||
'$flow': {args:['string']},
|
'$flowContext': {args:['string']},
|
||||||
'$global': {args:['string']},
|
'$globalContext': {args:['string']},
|
||||||
'$join':{ args:['array','separator'] },
|
'$join':{ args:['array','separator'] },
|
||||||
'$keys':{ args:['object'] },
|
'$keys':{ args:['object'] },
|
||||||
'$length':{ args:['string'] },
|
'$length':{ args:['string'] },
|
||||||
|
@ -519,7 +519,7 @@
|
|||||||
"else":"otherwise"
|
"else":"otherwise"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-expr": "Invalid expression: __error__"
|
"invalid-expr": "Invalid JSONata expression: __error__"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"change": {
|
"change": {
|
||||||
@ -544,7 +544,8 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-from": "Invalid 'from' property: __error__",
|
"invalid-from": "Invalid 'from' property: __error__",
|
||||||
"invalid-json": "Invalid 'to' JSON property"
|
"invalid-json": "Invalid 'to' JSON property",
|
||||||
|
"invalid-expr": "Invalid JSONata expression: __error__"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"range": {
|
"range": {
|
||||||
|
@ -104,7 +104,7 @@ module.exports = function(RED) {
|
|||||||
try {
|
try {
|
||||||
var prop;
|
var prop;
|
||||||
if (node.propertyType === 'jsonata') {
|
if (node.propertyType === 'jsonata') {
|
||||||
prop = node.property.evaluate({msg:msg});
|
prop = RED.util.evaluateJSONataExpression(node.property,msg);
|
||||||
} else {
|
} else {
|
||||||
prop = RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg);
|
prop = RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg);
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ module.exports = function(RED) {
|
|||||||
v1 = node.previousValue;
|
v1 = node.previousValue;
|
||||||
} else if (rule.vt === 'jsonata') {
|
} else if (rule.vt === 'jsonata') {
|
||||||
try {
|
try {
|
||||||
v1 = rule.v.evaluate({msg:msg});
|
v1 = RED.util.evaluateJSONataExpression(rule.v,msg);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
|
node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||||
return;
|
return;
|
||||||
@ -134,7 +134,7 @@ module.exports = function(RED) {
|
|||||||
v2 = node.previousValue;
|
v2 = node.previousValue;
|
||||||
} else if (rule.v2t === 'jsonata') {
|
} else if (rule.v2t === 'jsonata') {
|
||||||
try {
|
try {
|
||||||
v2 = rule.v2.evaluate({msg:msg});
|
v2 = RED.util.evaluateJSONataExpression(rule.v2,msg);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
|
node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||||
return;
|
return;
|
||||||
|
@ -19,6 +19,7 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
function ChangeNode(n) {
|
function ChangeNode(n) {
|
||||||
RED.nodes.createNode(this, n);
|
RED.nodes.createNode(this, n);
|
||||||
|
var node = this;
|
||||||
|
|
||||||
this.rules = n.rules;
|
this.rules = n.rules;
|
||||||
var rule;
|
var rule;
|
||||||
@ -90,7 +91,7 @@ module.exports = function(RED) {
|
|||||||
rule.to = RED.util.prepareJSONataExpression(rule.to,this);
|
rule.to = RED.util.prepareJSONataExpression(rule.to,this);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
valid = false;
|
valid = false;
|
||||||
this.error(RED._("change.errors.invalid-from",{error:e.message}));
|
this.error(RED._("change.errors.invalid-expr",{error:e.message}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +116,12 @@ module.exports = function(RED) {
|
|||||||
} else if (rule.tot === 'date') {
|
} else if (rule.tot === 'date') {
|
||||||
value = Date.now();
|
value = Date.now();
|
||||||
} else if (rule.tot === 'jsonata') {
|
} else if (rule.tot === 'jsonata') {
|
||||||
value = rule.to.evaluate({msg:msg});
|
try{
|
||||||
|
value = RED.util.evaluateJSONataExpression(rule.to,msg);
|
||||||
|
} catch(err) {
|
||||||
|
node.error(RED._("change.errors.invalid-expr",{error:err.message}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (rule.t === 'change') {
|
if (rule.t === 'change') {
|
||||||
if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') {
|
if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') {
|
||||||
@ -219,7 +225,6 @@ module.exports = function(RED) {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
if (valid) {
|
if (valid) {
|
||||||
var node = this;
|
|
||||||
this.on('input', function(msg) {
|
this.on('input', function(msg) {
|
||||||
for (var i=0; i<this.rules.length; i++) {
|
for (var i=0; i<this.rules.length; i++) {
|
||||||
if (this.rules[i].t === "move") {
|
if (this.rules[i].t === "move") {
|
||||||
|
@ -398,9 +398,15 @@
|
|||||||
"expressionEditor": {
|
"expressionEditor": {
|
||||||
"functions": "Functions",
|
"functions": "Functions",
|
||||||
"insert": "Insert",
|
"insert": "Insert",
|
||||||
"title": "JSONata Expression editor"
|
"title": "JSONata Expression editor",
|
||||||
|
"data": "Example message",
|
||||||
|
"result": "Result",
|
||||||
|
"format": "format expression",
|
||||||
|
"compatMode": "Compatibility mode enabled",
|
||||||
|
"compatModeDesc": "<h3>JSONata compatibility mode</h3><p> The current expression appears to still reference <code>msg</code> so will be evaluated in compatibility mode. Please update the expression to not use <code>msg</code> as this mode will be removed in the future.</p><p> When JSONata support was first added to Node-RED, it required the expression to reference the <code>msg</code> object. For example <code>msg.payload</code> would be used to access the payload.</p><p> That is no longer necessary as the expression will be evaluated against the message directly. To access the payload, the expression should be just <code>payload</code>.</p>"
|
||||||
},
|
},
|
||||||
"jsonEditor": {
|
"jsonEditor": {
|
||||||
"title": "JSON editor"
|
"title": "JSON editor",
|
||||||
|
"format": "format JSON"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,16 +110,11 @@
|
|||||||
"args": "object",
|
"args": "object",
|
||||||
"desc": "Splits an object containing key/value pairs into an array of objects, each of which has a single key/value pair from the input object. If the parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array."
|
"desc": "Splits an object containing key/value pairs into an array of objects, each of which has a single key/value pair from the input object. If the parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array."
|
||||||
},
|
},
|
||||||
|
"$flowContext": {
|
||||||
"$context": {
|
|
||||||
"args": "string",
|
|
||||||
"desc": "Retrieves a node context property."
|
|
||||||
},
|
|
||||||
"$flow": {
|
|
||||||
"args": "string",
|
"args": "string",
|
||||||
"desc": "Retrieves a flow context property."
|
"desc": "Retrieves a flow context property."
|
||||||
},
|
},
|
||||||
"$global": {
|
"$globalContext": {
|
||||||
"args": "string",
|
"args": "string",
|
||||||
"desc": "Retrieves a global context property."
|
"desc": "Retrieves a global context property."
|
||||||
}
|
}
|
||||||
|
@ -323,25 +323,33 @@ function evaluateNodeProperty(value, type, node, msg) {
|
|||||||
} else if (type === 'bool') {
|
} else if (type === 'bool') {
|
||||||
return /^true$/i.test(value);
|
return /^true$/i.test(value);
|
||||||
} else if (type === 'jsonata') {
|
} else if (type === 'jsonata') {
|
||||||
return prepareJSONataExpression(value,node).evaluate({msg:msg});
|
var expr = prepareJSONataExpression(value,node);
|
||||||
|
return evaluateJSONataExpression(expr,msg);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareJSONataExpression(value,node) {
|
function prepareJSONataExpression(value,node) {
|
||||||
var expr = jsonata(value);
|
var expr = jsonata(value);
|
||||||
expr.assign('context',function(val) {
|
expr.assign('flowContext',function(val) {
|
||||||
return node.context().get(val);
|
|
||||||
});
|
|
||||||
expr.assign('flow',function(val) {
|
|
||||||
return node.context().flow.get(val);
|
return node.context().flow.get(val);
|
||||||
});
|
});
|
||||||
expr.assign('global',function(val) {
|
expr.assign('globalContext',function(val) {
|
||||||
return node.context().global(val);
|
return node.context().global(val);
|
||||||
});
|
});
|
||||||
|
expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function evaluateJSONataExpression(expr,msg) {
|
||||||
|
var context = msg;
|
||||||
|
if (expr._legacyMode) {
|
||||||
|
context = {msg:msg};
|
||||||
|
}
|
||||||
|
return expr.evaluate(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function normaliseNodeTypeName(name) {
|
function normaliseNodeTypeName(name) {
|
||||||
var result = name.replace(/[^a-zA-Z0-9]/g, " ");
|
var result = name.replace(/[^a-zA-Z0-9]/g, " ");
|
||||||
result = result.trim();
|
result = result.trim();
|
||||||
@ -366,5 +374,6 @@ module.exports = {
|
|||||||
evaluateNodeProperty: evaluateNodeProperty,
|
evaluateNodeProperty: evaluateNodeProperty,
|
||||||
normalisePropertyExpression: normalisePropertyExpression,
|
normalisePropertyExpression: normalisePropertyExpression,
|
||||||
normaliseNodeTypeName: normaliseNodeTypeName,
|
normaliseNodeTypeName: normaliseNodeTypeName,
|
||||||
prepareJSONataExpression: prepareJSONataExpression
|
prepareJSONataExpression: prepareJSONataExpression,
|
||||||
|
evaluateJSONataExpression: evaluateJSONataExpression
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user