diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 43b0137c4..9a278b223 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,9 @@ jobs: with: node-version: '12' - run: node ./node-red/.github/scripts/update-node-red-docker.js + with: + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true - name: Create Docker Pull Request uses: peter-evans/create-pull-request@v2 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c3ab41b3..e9d7ba010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +### 1.2.5: Maintenance Release + +Editor + + - Fix import of config nodes with unknown z property + +Runtime + + - Set ACTIONS_ALLOW_UNSECURE_COMMANDS in GH Action + +### 1.2.4: Maintenance Release + +Editor + + - Support bigint types in Debug sidebar + - Clear retained status of deleted nodes + - Prevent needless retention of node status messages + - Update projects dialogs to use TypedInput-cred input + - Restore cursor position in TypedInput cred-mode + - Ensure config nodes with invalid z are imported somewhere + - Ensure user keyboard shortcuts override defaults Fixes #2753 + +Runtime + + - Disable projects when flowFile passed into grunt dev + - Add Russian Locale (#2761) (#2531) (@alexk111) + - Add Japanese translation for http-in node (#2758) (@kazuhitoyokoi) + +Nodes + + - CSV: Fix CSV node repeating array output + ### 1.2.3: Maintenance Release Editor diff --git a/Gruntfile.js b/Gruntfile.js index 49d2f154e..6272e9818 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -24,6 +24,7 @@ module.exports = function(grunt) { var flowFile = grunt.option('flowFile'); if (flowFile) { nodemonArgs.push(flowFile); + process.env.NODE_RED_ENABLE_PROJECTS=false; } var userDir = grunt.option('userDir'); if (userDir) { diff --git a/package.json b/package.json index 4f3c0bb00..15abf0507 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,8 @@ "media-typer": "1.1.0", "memorystore": "1.6.4", "mime": "2.4.6", - "moment-timezone": "^0.5.31", - "mqtt": "4.2.4", + "moment-timezone": "0.5.32", + "mqtt": "4.2.5", "multer": "1.4.2", "mustache": "4.0.1", "node-red-admin": "^0.2.6", @@ -73,7 +73,7 @@ "request": "2.88.0", "semver": "6.3.0", "tar": "6.0.5", - "uglify-js": "3.11.4", + "uglify-js": "3.11.6", "when": "3.7.8", "ws": "6.2.1", "xml2js": "0.4.23" @@ -104,7 +104,7 @@ "grunt-simple-nyc": "^3.0.1", "http-proxy": "1.18.1", "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", - "marked": "1.2.2", + "marked": "1.2.4", "minami": "1.2.3", "mocha": "^5.2.0", "node-red-node-test-helper": "^0.2.5", diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/comms.js b/packages/node_modules/@node-red/editor-api/lib/editor/comms.js index 386b5f8a8..9c96bbf5b 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/comms.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/comms.js @@ -33,8 +33,6 @@ var activeConnections = []; var anonymousUser; -var retained = {}; - var heartbeatTimer; var lastSentTime; diff --git a/packages/node_modules/@node-red/editor-client/locales/ru/editor.json b/packages/node_modules/@node-red/editor-client/locales/ru/editor.json index e6522b27a..1f91d4b0b 100755 --- a/packages/node_modules/@node-red/editor-client/locales/ru/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ru/editor.json @@ -42,7 +42,8 @@ "loadNodeCatalogs": "Загрузка каталогов узлов", "loadNodes": "Загрузка узлов __count__", "loadFlows": "Загрузка потоков", - "importFlows": "Добавление потоков в рабочую область" + "importFlows": "Добавление потоков в рабочую область", + "importError": "

Ошибка добавления потоков

__message__

" }, "workspace": { "defaultName": "Поток __number__", @@ -214,6 +215,9 @@ "importUnrecognised": "Импортирован неопознанный тип:", "importUnrecognised_plural_2": "Импортированы неопознанные типы:", "importUnrecognised_plural_5": "Импортированы неопознанные типы:", + "importDuplicate": "Импортирован дубликат узла:", + "importDuplicate_plural_2": "Импортированы дубликаты узлов:", + "importDuplicate_plural_5": "Импортированы дубликаты узлов:", "nodesExported": "Узлы экспортированы в буфер обмена", "nodesImported": "Импортированы:", "nodeCopied": "__count__ узел скопирован", diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 0d620e70a..ddeea65e6 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -1359,6 +1359,10 @@ RED.nodes = (function() { } } } + } else { + if (n.z && !workspaces[n.z]) { + n.z = activeWorkspace; + } } if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js index b7c25a14f..e46c88b4f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js @@ -773,6 +773,9 @@ RED.clipboard = (function() { // representation or null return null; } + if (value.type === 'bigint') { + return value.data.toString(); + } if (value.type === 'undefined') { return undefined; } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js index 037e8e454..fa1e68cca 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js @@ -173,6 +173,7 @@ valueLabel: function(container,value) { var that = this; container.css("pointer-events","none"); + container.css("flex-grow",0); this.elementDiv.hide(); var buttons = $('
').css({ position: "absolute", @@ -184,22 +185,25 @@ width:"20px" }).appendTo(buttons).on("click", function(evt) { evt.preventDefault(); + var cursorPosition = that.input[0].selectionStart; var currentType = that.input.attr("type"); if (currentType === "text") { that.input.attr("type","password"); eyeCon.removeClass("fa-eye-slash").addClass("fa-eye"); setTimeout(function() { that.input.focus(); + that.input[0].setSelectionRange(cursorPosition, cursorPosition); },50); } else { that.input.attr("type","text"); eyeCon.removeClass("fa-eye").addClass("fa-eye-slash"); setTimeout(function() { that.input.focus(); + that.input[0].setSelectionRange(cursorPosition, cursorPosition); },50); } }).hide(); - var eyeCon = $('').css("margin-left","-1px").appendTo(eyeButton); + var eyeCon = $('').css("margin-left","-2px").appendTo(eyeButton); if (value === "__PWRD__") { var innerContainer = $('
').css({ @@ -284,7 +288,7 @@ this.input.css('width','100%'); this.uiSelect.width(m[1]); this.uiWidth = null; - } else { + } else if (this.uiWidth !== 0){ this.uiSelect.width(this.uiWidth); } ["Right","Left"].forEach(function(d) { @@ -304,7 +308,11 @@ this.element.attr('type','hidden'); - this.options.types = this.options.types||Object.keys(allOptions); + if (!this.options.types && this.options.type) { + this.options.types = [this.options.type] + } else { + this.options.types = this.options.types||Object.keys(allOptions); + } this.selectTrigger = $('').prependTo(this.uiSelect); $('').toggle(this.options.types.length > 1).appendTo(this.selectTrigger); @@ -872,6 +880,9 @@ this.elementDiv.hide(); this.valueLabelContainer.hide(); } else if (opt.valueLabel) { + // Reset any CSS the custom label may have set + this.valueLabelContainer.css("pointer-events",""); + this.valueLabelContainer.css("flex-grow",1); this.valueLabelContainer.show(); this.valueLabelContainer.empty(); this.elementDiv.hide(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js index 4abe6fd1f..fdd106e23 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js @@ -1033,7 +1033,7 @@ RED.projects.settings = (function() { var credentialSecretExistingRow = $('
').appendTo(credentialFormRows); $('').text(RED._("sidebar.project.projectSettings.currentKey")).appendTo(credentialSecretExistingRow); - var credentialSecretExistingInput = $('').appendTo(credentialSecretExistingRow) + var credentialSecretExistingInput = $('').appendTo(credentialSecretExistingRow).typedInput({type:"cred"}) .on("change keyup paste",function() { if (popover) { popover.close(); @@ -1046,7 +1046,7 @@ RED.projects.settings = (function() { $('').text(RED._("sidebar.project.projectSettings.newKey")).appendTo(credentialSecretNewRow); - var credentialSecretNewInput = $('').appendTo(credentialSecretNewRow).on("change keyup paste",checkFiles); + var credentialSecretNewInput = $('').appendTo(credentialSecretNewRow).typedInput({type:"cred"}).on("change keyup paste",checkFiles); var credentialResetWarning = $('
' + RED._("sidebar.project.projectSettings.credentialsAlert") + '
').hide().appendTo(credentialFormRows); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js index 13f9dcb02..7a77217d5 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js @@ -81,8 +81,8 @@ RED.projects = (function() { $('

').text(RED._("projects.welcome.desc2")).appendTo(body); var row = $('

').appendTo(body); - var createAsEmpty = $('').appendTo(row); - var createAsClone = $('').appendTo(row); + var createAsEmpty = $('').appendTo(row); + var createAsClone = $('').appendTo(row); createAsEmpty.on("click", function(e) { e.preventDefault(); @@ -511,7 +511,8 @@ RED.projects = (function() { subrow = $('
').appendTo(row); $('').appendTo(subrow); - projectRepoPasswordInput = $('').appendTo(subrow); + projectRepoPasswordInput = $('').appendTo(subrow); + projectRepoPasswordInput.typedInput({type:"cred"}); // ----------------------------------------------------- // Repo credentials - key/passphrase ------------------- @@ -539,12 +540,12 @@ RED.projects = (function() { subrow = $('
').appendTo(row); $('').appendTo(subrow); projectRepoPassphrase = $('').appendTo(subrow); - + projectRepoPassphrase.typedInput({type:"cred"}); subrow = $('
').appendTo(cloneAuthRows); var sshwarningRow = $('
').hide().appendTo(subrow); $('
'+RED._("projects.clone-project.ssh-key-desc")+'
').appendTo(sshwarningRow); subrow = $('
').appendTo(sshwarningRow); - $('').appendTo(subrow).on("click", function(e) { + $('').appendTo(subrow).on("click", function(e) { e.preventDefault(); dialog.dialog( "close" ); RED.userSettings.show('gitconfig'); @@ -558,8 +559,8 @@ RED.projects = (function() { // Secret - clone row = $('
').appendTo(body); $('').appendTo(row); - projectSecretInput = $('').appendTo(row); - + projectSecretInput = $('').appendTo(row); + projectSecretInput.typedInput({type:"cred"}); return container; @@ -894,6 +895,7 @@ RED.projects = (function() { $('').appendTo(row); row = $('
').appendTo(credentialsRightBox); emptyProjectCredentialInput = $('').appendTo(row); + emptyProjectCredentialInput.typedInput({type:"cred"}); emptyProjectCredentialInput.on("change keyup paste", validateForm); row = $('
').hide().appendTo(credentialsRightBox); @@ -1169,11 +1171,11 @@ RED.projects = (function() { row = $('
').appendTo(container); - var openProject = $('').appendTo(row); - var createAsEmpty = $('').appendTo(row); - // var createAsCopy = $('').appendTo(row); - var createAsClone = $('').appendTo(row); - // var createAsClone = $('').appendTo(row); + var openProject = $('').appendTo(row); + var createAsEmpty = $('').appendTo(row); + // var createAsCopy = $('').appendTo(row); + var createAsClone = $('').appendTo(row); + // var createAsClone = $('').appendTo(row); row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) { evt.preventDefault(); container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected'); @@ -1298,6 +1300,7 @@ RED.projects = (function() { $('').appendTo(row); // row = $('
').appendTo(credentialsRightBox); emptyProjectCredentialInput = $('').appendTo(row); + emptyProjectCredentialInput.typedInput({type:"cred"}); emptyProjectCredentialInput.on("change keyup paste", validateForm); $('').appendTo(row); @@ -1356,7 +1359,8 @@ RED.projects = (function() { subrow = $('
').appendTo(row); $('').appendTo(subrow); - projectRepoPasswordInput = $('').appendTo(subrow); + projectRepoPasswordInput = $('').appendTo(subrow); + projectRepoPasswordInput.typedInput({type:"cred"}); // ----------------------------------------------------- // Repo credentials - key/passphrase ------------------- @@ -1384,12 +1388,13 @@ RED.projects = (function() { subrow = $('
').appendTo(row); $('').appendTo(subrow); projectRepoPassphrase = $('').appendTo(subrow); + projectRepoPassphrase.typedInput({type:"cred"}); subrow = $('
').appendTo(cloneAuthRows); var sshwarningRow = $('
').hide().appendTo(subrow); $('
'+RED._("projects.create.desc2")+'
').appendTo(sshwarningRow); subrow = $('
').appendTo(sshwarningRow); - $('').appendTo(subrow).on("click", function(e) { + $('').appendTo(subrow).on("click", function(e) { e.preventDefault(); $('#red-ui-projects-dialog-cancel').trigger("click"); RED.userSettings.show('gitconfig'); @@ -1403,8 +1408,8 @@ RED.projects = (function() { // Secret - clone row = $('
').appendTo(container); $('').appendTo(row); - projectSecretInput = $('').appendTo(row); - + projectSecretInput = $('').appendTo(row); + projectSecretInput.typedInput({type:"cred"}); switch(options.screen||"empty") { case "empty": createAsEmpty.trigger("click"); break; @@ -1617,14 +1622,14 @@ RED.projects = (function() { function deleteProject(row,name,done) { var cover = $('
').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row); $('').text(RED._("projects.delete.confirm")).appendTo(cover); - $('') + $('') .appendTo(cover) .on("click", function(e) { e.stopPropagation(); cover.remove(); done(true); }); - $('') + $('') .appendTo(cover) .on("click", function(e) { e.stopPropagation(); @@ -1808,7 +1813,7 @@ RED.projects = (function() { header.addClass("selectable"); var tools = $('
').appendTo(header); - $('') + $('') .appendTo(tools) .on("click", function(e) { e.stopPropagation(); @@ -1962,7 +1967,8 @@ RED.projects = (function() { var isSSH = false; if (/^https?:\/\//.test(url)) { $('
'+ - '
').appendTo(message); + '
').appendTo(message); + message.find("#projects-user-auth-password").typedInput({type:"cred"}) } else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) { isSSH = true; var row = $('
').appendTo(message); @@ -1980,7 +1986,7 @@ RED.projects = (function() { }); row = $('
').appendTo(message); $('').appendTo(row); - $('').appendTo(row); + $('').appendTo(row).typedInput({type:"cred"}); } var notification = RED.notify(message,{ diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index 6227fa683..230f561f9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -60,7 +60,7 @@ RED.utils = (function() { result = $('').text('array['+value.length+']'); } else if (value.hasOwnProperty('type') && value.type === 'function') { result = $('').text('function'); - } else if (value.hasOwnProperty('type') && value.type === 'number') { + } else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) { result = $('').text(value.data); } else { result = $('object'); @@ -352,7 +352,7 @@ RED.utils = (function() { $(''+obj+'').appendTo(entryObj); } else if (obj.__enc__ && obj.type === 'undefined') { $('undefined').appendTo(entryObj); - } else if (obj.__enc__ && obj.type === 'number') { + } else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) { e = $('').text(obj.data).appendTo(entryObj); } else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) { e = $('').text("function").appendTo(entryObj); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/projects.scss b/packages/node_modules/@node-red/editor-client/src/sass/projects.scss index e42125c23..6a8e73938 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/projects.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/projects.scss @@ -90,7 +90,7 @@ font-size: 1.2em; } } - button.red-ui-button { + button.red-ui-button.red-ui-projects-dialog-button { width: calc(50% - 80px); margin: 20px; height: auto; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss index ec865b116..fb3e331ee 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss @@ -18,6 +18,7 @@ border: 1px solid $form-input-border-color; border-radius: 4px; height: 34px; + line-height: 14px; display: inline-flex; padding: 0; margin: 0; diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.js b/packages/node_modules/@node-red/nodes/core/common/21-debug.js index 272f48016..0e00a8fff 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.js +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.js @@ -21,8 +21,11 @@ module.exports = function(RED) { this.tosidebar = n.tosidebar; if (this.tosidebar === undefined) { this.tosidebar = true; } this.active = (n.active === null || typeof n.active === "undefined") || n.active; - if (this.tostatus) { this.status({fill:"grey", shape:"ring"}); } - else { this.status({}); } + if (this.tostatus) { + this.status({fill:"grey", shape:"ring"}); + this.oldState = "{}"; + } + var hasStatExpression = (n.statusType === "jsonata"); var statExpression = hasStatExpression ? n.statusVal : null; @@ -97,7 +100,11 @@ module.exports = function(RED) { } } } - + this.on("close", function() { + if (this.oldState) { + this.status({}); + } + }) this.on("input", function(msg, send, done) { if (msg.hasOwnProperty("status") && msg.status.hasOwnProperty("source") && msg.status.source.hasOwnProperty("id") && (msg.status.source.id === node.id)) { done(); @@ -202,13 +209,8 @@ module.exports = function(RED) { function setNodeState(node,state) { if (state) { node.active = true; - if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); } } else { node.active = false; - if (node.tostatus && node.hasOwnProperty("oldStatus")) { - node.oldStatus.shape = "dot"; - node.status(node.oldStatus); - } } } diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 69b0c202a..eb07c319d 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -127,6 +127,8 @@ module.exports = function(RED) { node.topic = n.topic; node.outstandingTimers = []; node.outstandingIntervals = []; + node.clearStatus = false; + var sandbox = { console:console, util:util, @@ -163,6 +165,7 @@ module.exports = function(RED) { node.on.apply(node, arguments); }, status: function() { + node.clearStatus = true; node.status.apply(node, arguments); } }, @@ -389,7 +392,9 @@ module.exports = function(RED) { while (node.outstandingIntervals.length > 0) { clearInterval(node.outstandingIntervals.pop()); } - node.status({}); + if (node.clearStatus) { + node.status({}); + } }); promise.then(function (v) { diff --git a/packages/node_modules/@node-red/nodes/locales/ru/parsers/70-CSV.html b/packages/node_modules/@node-red/nodes/locales/ru/parsers/70-CSV.html index aa616110f..de94a1fd8 100644 --- a/packages/node_modules/@node-red/nodes/locales/ru/parsers/70-CSV.html +++ b/packages/node_modules/@node-red/nodes/locales/ru/parsers/70-CSV.html @@ -46,7 +46,7 @@ При преобразовании в CSV шаблон столбцов используется для определения того, какие свойства извлекать из объекта и в каком порядке.

- Если шаблон пуст, то узел может использовать простой список свойств, разделенных запятыми, предоставленных в msg.columns, чтобы определить, что извлечь. Если этого нет, то все свойства объекта выводятся в том порядке, в котором они были найдены. + Если шаблон пуст, то узел может использовать простой список свойств, разделенных запятыми, предоставленных в msg.columns, чтобы определить, что извлечь. Если этого нет, то все свойства объекта выводятся в том порядке, в котором они были найдены в первой строке.

Если входные данные являются массивом, то шаблон столбцов используется только для необязательного генерирования строки с заголовками столбцов. diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index ad10ebc65..bf7f3a362 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -31,7 +31,7 @@ "is-utf8": "0.2.1", "js-yaml": "3.14.0", "media-typer": "1.1.0", - "mqtt": "4.2.4", + "mqtt": "4.2.5", "multer": "1.4.2", "mustache": "4.0.1", "on-headers": "1.0.2", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 691b55f3c..47472f38e 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -19,7 +19,7 @@ "@node-red/util": "1.3.0-beta.1", "semver": "6.3.0", "tar": "6.0.5", - "uglify-js": "3.11.4", + "uglify-js": "3.11.6", "when": "3.7.8" } } diff --git a/packages/node_modules/@node-red/runtime/lib/api/comms.js b/packages/node_modules/@node-red/runtime/lib/api/comms.js index 0652a651b..55789b806 100644 --- a/packages/node_modules/@node-red/runtime/lib/api/comms.js +++ b/packages/node_modules/@node-red/runtime/lib/api/comms.js @@ -38,12 +38,20 @@ function handleCommsEvent(event) { publish(event.topic,event.data,event.retain); } function handleStatusEvent(event) { - var status = { - text: event.status.text, - fill: event.status.fill, - shape: event.status.shape - }; - publish("status/"+event.id,status,true); + if (!event.status) { + delete retained["status/"+event.id] + } else if (!event.status.text && !event.status.fill && !event.status.shape) { + if (retained["status/"+event.id]) { + publish("status/"+event.id,{},false); + } + } else { + var status = { + text: event.status.text, + fill: event.status.fill, + shape: event.status.shape + }; + publish("status/"+event.id,status,true); + } } function handleRuntimeEvent(event) { runtime.log.trace("runtime event: "+JSON.stringify(event)); diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index 20a4f6c3d..a7e00bfbc 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -319,6 +319,11 @@ class Flow { node.error(err); } } + if (removedMap[stopList[i]]) { + events.emit("node-status",{ + id: node.id + }); + } } } return Promise.all(promises); diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js index 8b78cad17..78ec9ee75 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js @@ -18,6 +18,7 @@ const clone = require("clone"); const Flow = require('./Flow').Flow; const context = require('../nodes/context'); const util = require("util"); +const events = require("../events"); const redUtil = require("@node-red/util").util; const flowUtil = require("./util"); @@ -308,7 +309,26 @@ class Subflow extends Flow { super.start(diff); } + /** + * Stop this subflow. + * The `stopList` argument helps define what needs to be stopped in the case + * of a modified-nodes/flows type deploy. + * @param {[type]} stopList [description] + * @param {[type]} removedList [description] + * @return {[type]} [description] + */ + stop(stopList, removedList) { + const nodes = Object.keys(this.activeNodes); + return super.stop(stopList, removedList).then(res => { + nodes.forEach(id => { + events.emit("node-status",{ + id: id + }); + }) + return res; + }) + } /** * Get environment variable of subflow * @param {String} name name of env var diff --git a/packages/node_modules/@node-red/util/lib/util.js b/packages/node_modules/@node-red/util/lib/util.js index 261dc3867..07f506007 100644 --- a/packages/node_modules/@node-red/util/lib/util.js +++ b/packages/node_modules/@node-red/util/lib/util.js @@ -774,6 +774,12 @@ function encodeObject(msg,opts) { data: value.toString() } } + } else if (typeof value === 'bigint') { + value = { + __enc__: true, + type: 'bigint', + data: value.toString() + } } else if (value && value.constructor) { if (value.type === "Buffer") { value.__enc__ = true; @@ -808,6 +814,13 @@ function encodeObject(msg,opts) { } else if (msgType === "number") { msg.format = "number"; msg.msg = msg.msg.toString(); + } else if (msgType === "bigint") { + msg.format = "bigint"; + msg.msg = { + __enc__: true, + type: 'bigint', + data: msg.msg.toString() + }; } else if (msg.msg === null || msgType === "undefined") { msg.format = (msg.msg === null)?"null":"undefined"; msg.msg = "(undefined)"; diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index fc5d48849..9d447a280 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -20,7 +20,7 @@ "json-stringify-safe": "5.0.1", "jsonata": "1.8.4", "lodash.clonedeep": "^4.5.0", - "moment-timezone": "^0.5.31", + "moment-timezone": "0.5.32", "when": "3.7.8" } } diff --git a/test/unit/@node-red/runtime/lib/api/comms_spec.js b/test/unit/@node-red/runtime/lib/api/comms_spec.js index df423b7dd..e1359cb7d 100644 --- a/test/unit/@node-red/runtime/lib/api/comms_spec.js +++ b/test/unit/@node-red/runtime/lib/api/comms_spec.js @@ -211,6 +211,82 @@ describe("runtime-api/comms", function() { }); }).catch(done); }) + it('retains non-blank status message',function(done){ + eventHandlers['node-status']({ + id: "node1234", + status: {text:"hello"} + }) + messages.should.have.length(0); + comms.addConnection({client: clientConnection}).then(function() { + return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { + messages.should.have.length(1); + messages[0].should.have.property("topic","status/node1234"); + messages[0].should.have.property("data",{text:"hello", fill: undefined, shape: undefined}); + done(); + }); + }).catch(done); + }) + it('does not retain blank status message',function(done){ + eventHandlers['node-status']({ + id: "node1234", + status: {} + }) + messages.should.have.length(0); + comms.addConnection({client: clientConnection}).then(function() { + return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { + messages.should.have.length(0); + done(); + }); + }).catch(done); + }) + it('does not send blank status if first status',function(done){ + messages.should.have.length(0); + comms.addConnection({client: clientConnection}).then(function() { + return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { + eventHandlers['node-status']({ + id: "node5678", + status: {} + }) + messages.should.have.length(0); + done() + }) + }).catch(done); + }); + it('sends blank status if replacing retained',function(done){ + eventHandlers['node-status']({ + id: "node5678", + status: {text:"hello"} + }) + messages.should.have.length(0); + comms.addConnection({client: clientConnection}).then(function() { + return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { + messages.should.have.length(1); + eventHandlers['node-status']({ + id: "node5678", + status: {} + }) + messages.should.have.length(2); + done() + }) + }).catch(done); + }); + + it('does not retain initial status blank message',function(done){ + eventHandlers['node-status']({ + id: "my-event", + status: {} + }) + messages.should.have.length(0); + comms.addConnection({client: clientConnection}).then(function() { + return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() { + messages.should.have.length(1); + messages[0].should.have.property("topic","my-event"); + messages[0].should.have.property("data","my-payload"); + done(); + }); + }).catch(done); + }) + it('retained messages get cleared',function(done) { eventHandlers['comms']({ topic: "my-event",