From 16c26d8098bfe482b48007e32bc4253c79a53201 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 31 Jul 2020 15:26:21 +0100 Subject: [PATCH] Move runtime settings back to adminApi from editorApi Fixes #2662 --- .../@node-red/editor-api/lib/admin/index.js | 6 +- .../editor-api/lib/admin/settings.js | 72 ++++++++++++++ .../@node-red/editor-api/lib/editor/index.js | 2 +- .../editor-api/lib/editor/settings.js | 44 --------- .../@node-red/editor-api/lib/index.js | 2 +- .../@node-red/runtime/lib/api/settings.js | 54 +++++------ .../editor-api/lib/admin/index_spec.js | 2 +- .../editor-api/lib/admin/settings_spec.js | 93 +++++++++++++++++++ .../editor-api/lib/editor/settings_spec.js | 26 ------ .../runtime/lib/api/settings_spec.js | 51 ++++++++++ 10 files changed, 252 insertions(+), 100 deletions(-) create mode 100644 packages/node_modules/@node-red/editor-api/lib/admin/settings.js create mode 100644 test/unit/@node-red/editor-api/lib/admin/settings_spec.js diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/index.js b/packages/node_modules/@node-red/editor-api/lib/admin/index.js index 50d7b168f..d982c560d 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/index.js @@ -21,15 +21,17 @@ var flows = require("./flows"); var flow = require("./flow"); var context = require("./context"); var auth = require("../auth"); +var info = require("./settings"); var apiUtil = require("../util"); module.exports = { - init: function(runtimeAPI) { + init: function(settings,runtimeAPI) { flows.init(runtimeAPI); flow.init(runtimeAPI); nodes.init(runtimeAPI); context.init(runtimeAPI); + info.init(settings,runtimeAPI); var needsPermission = auth.needsPermission; @@ -67,6 +69,8 @@ module.exports = { // adminApp.delete("/context/:scope(node|flow)/:id",needsPermission("context.write"),context.delete,apiUtil.errorHandler); adminApp.delete("/context/:scope(node|flow)/:id/*",needsPermission("context.write"),context.delete,apiUtil.errorHandler); + adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler); + return adminApp; } } diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/settings.js b/packages/node_modules/@node-red/editor-api/lib/admin/settings.js new file mode 100644 index 000000000..d72f9e094 --- /dev/null +++ b/packages/node_modules/@node-red/editor-api/lib/admin/settings.js @@ -0,0 +1,72 @@ +/** + * 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. + **/ +var apiUtils = require("../util"); +var runtimeAPI; +var settings; +var theme = require("../editor/theme"); +var clone = require("clone"); + +var i18n = require("@node-red/util").i18n + +function extend(target, source) { + var keys = Object.keys(source); + var i = keys.length; + while(i--) { + var value = source[keys[i]] + var type = typeof value; + if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) { + target[keys[i]] = value; + } else if (value === null) { + if (target.hasOwnProperty(keys[i])) { + delete target[keys[i]]; + } + } else { + // Object + if (target.hasOwnProperty(keys[i])) { + target[keys[i]] = extend(target[keys[i]],value); + } else { + target[keys[i]] = value; + } + } + } + return target; +} + +module.exports = { + init: function(_settings,_runtimeAPI) { + runtimeAPI = _runtimeAPI; + settings = _settings; + }, + runtimeSettings: function(req,res) { + var opts = { + user: req.user + } + runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) { + if (!settings.disableEditor) { + result.editorTheme = result.editorTheme||{}; + var themeSettings = theme.settings(); + if (themeSettings) { + // result.editorTheme may already exist with the palette + // disabled. Need to merge that into the receive settings + result.editorTheme = extend(clone(themeSettings),result.editorTheme); + } + result.editorTheme.languages = i18n.availableLanguages("editor"); + } + res.json(result); + }); + }, + +} diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/index.js b/packages/node_modules/@node-red/editor-api/lib/editor/index.js index a9cdf0ffc..71876eaa6 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/index.js @@ -103,7 +103,7 @@ module.exports = { editorApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get,apiUtil.errorHandler); // Settings - editorApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler); + // Main /settings route is an admin route - see lib/admin/settings.js // User Settings editorApp.get("/settings/user",needsPermission("settings.read"),info.userSettings,apiUtil.errorHandler); // User Settings diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/settings.js b/packages/node_modules/@node-red/editor-api/lib/editor/settings.js index 9d9867f1b..5fa2476e1 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/settings.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/settings.js @@ -16,56 +16,12 @@ var apiUtils = require("../util"); var runtimeAPI; var sshkeys = require("./sshkeys"); -var theme = require("./theme"); -var clone = require("clone"); - -var i18n = require("@node-red/util").i18n - -function extend(target, source) { - var keys = Object.keys(source); - var i = keys.length; - while(i--) { - var value = source[keys[i]] - var type = typeof value; - if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) { - target[keys[i]] = value; - } else if (value === null) { - if (target.hasOwnProperty(keys[i])) { - delete target[keys[i]]; - } - } else { - // Object - if (target.hasOwnProperty(keys[i])) { - target[keys[i]] = extend(target[keys[i]],value); - } else { - target[keys[i]] = value; - } - } - } - return target; -} module.exports = { init: function(_runtimeAPI) { runtimeAPI = _runtimeAPI; sshkeys.init(runtimeAPI); }, - runtimeSettings: function(req,res) { - var opts = { - user: req.user - } - runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) { - result.editorTheme = result.editorTheme||{}; - var themeSettings = theme.settings(); - if (themeSettings) { - // result.editorTheme may already exist with the palette - // disabled. Need to merge that into the receive settings - result.editorTheme = extend(clone(themeSettings),result.editorTheme); - } - result.editorTheme.languages = i18n.availableLanguages("editor"); - res.json(result); - }); - }, userSettings: function(req, res) { var opts = { user: req.user diff --git a/packages/node_modules/@node-red/editor-api/lib/index.js b/packages/node_modules/@node-red/editor-api/lib/index.js index 534a77869..457b99cc9 100644 --- a/packages/node_modules/@node-red/editor-api/lib/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/index.js @@ -99,7 +99,7 @@ function init(settings,_server,storage,runtimeAPI) { adminApp.use(corsHandler); } - var adminApiApp = require("./admin").init(runtimeAPI); + var adminApiApp = require("./admin").init(settings, runtimeAPI); adminApp.use(adminApiApp); } else { adminApp = null; diff --git a/packages/node_modules/@node-red/runtime/lib/api/settings.js b/packages/node_modules/@node-red/runtime/lib/api/settings.js index 96bb6c973..6ebeeccfb 100644 --- a/packages/node_modules/@node-red/runtime/lib/api/settings.js +++ b/packages/node_modules/@node-red/runtime/lib/api/settings.js @@ -81,39 +81,41 @@ var api = module.exports = { }) } - safeSettings.context = runtime.nodes.listContextStores(); + if (!runtime.settings.disableEditor) { + safeSettings.context = runtime.nodes.listContextStores(); - if (util.isArray(runtime.settings.paletteCategories)) { - safeSettings.paletteCategories = runtime.settings.paletteCategories; - } + if (util.isArray(runtime.settings.paletteCategories)) { + safeSettings.paletteCategories = runtime.settings.paletteCategories; + } - if (runtime.settings.flowFilePretty) { - safeSettings.flowFilePretty = runtime.settings.flowFilePretty; - } + if (runtime.settings.flowFilePretty) { + safeSettings.flowFilePretty = runtime.settings.flowFilePretty; + } - if (!runtime.nodes.paletteEditorEnabled()) { - safeSettings.editorTheme = safeSettings.editorTheme || {}; - safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {}; - safeSettings.editorTheme.palette.editable = false; - } - if (runtime.storage.projects) { - var activeProject = runtime.storage.projects.getActiveProject(); - if (activeProject) { - safeSettings.project = activeProject; - } else if (runtime.storage.projects.flowFileExists()) { - safeSettings.files = { - flow: runtime.storage.projects.getFlowFilename(), - credentials: runtime.storage.projects.getCredentialsFilename() + if (!runtime.nodes.paletteEditorEnabled()) { + safeSettings.editorTheme = safeSettings.editorTheme || {}; + safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {}; + safeSettings.editorTheme.palette.editable = false; + } + if (runtime.storage.projects) { + var activeProject = runtime.storage.projects.getActiveProject(); + if (activeProject) { + safeSettings.project = activeProject; + } else if (runtime.storage.projects.flowFileExists()) { + safeSettings.files = { + flow: runtime.storage.projects.getFlowFilename(), + credentials: runtime.storage.projects.getCredentialsFilename() + } + } + safeSettings.git = { + globalUser: runtime.storage.projects.getGlobalGitUser() } } - safeSettings.git = { - globalUser: runtime.storage.projects.getGlobalGitUser() - } + + safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType(); + runtime.settings.exportNodeSettings(safeSettings); } - safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType(); - - runtime.settings.exportNodeSettings(safeSettings); resolve(safeSettings); }catch(err) { diff --git a/test/unit/@node-red/editor-api/lib/admin/index_spec.js b/test/unit/@node-red/editor-api/lib/admin/index_spec.js index ead5f274a..4f9f2d55a 100644 --- a/test/unit/@node-red/editor-api/lib/admin/index_spec.js +++ b/test/unit/@node-red/editor-api/lib/admin/index_spec.js @@ -102,7 +102,7 @@ describe("api/admin/index", function() { }); before(function() { - app = adminApi.init({}); + app = adminApi.init({},{}); }); beforeEach(function() { diff --git a/test/unit/@node-red/editor-api/lib/admin/settings_spec.js b/test/unit/@node-red/editor-api/lib/admin/settings_spec.js new file mode 100644 index 000000000..d0c4cbe94 --- /dev/null +++ b/test/unit/@node-red/editor-api/lib/admin/settings_spec.js @@ -0,0 +1,93 @@ +/** + * 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. + **/ + +var should = require("should"); +var request = require('supertest'); +var express = require('express'); +var bodyParser = require("body-parser"); +var sinon = require('sinon'); + +var app; + +var NR_TEST_UTILS = require("nr-test-utils"); + +var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/settings"); +var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); + +describe("api/editor/settings", function() { + before(function() { + sinon.stub(theme,"settings",function() { return { existing: 123, test: 456 };}); + app = express(); + app.use(bodyParser.json()); + app.get("/settings",info.runtimeSettings); + }); + + after(function() { + theme.settings.restore(); + }); + + it('returns the runtime settings', function(done) { + info.init({},{ + settings: { + getRuntimeSettings: function(opts) { + return Promise.resolve({ + a:1, + b:2, + editorTheme: { existing: 789 } + }) + } + } + }); + request(app) + .get("/settings") + .expect(200) + .end(function(err,res) { + if (err) { + return done(err); + } + res.body.should.have.property("a",1); + res.body.should.have.property("b",2); + res.body.should.have.property("editorTheme",{existing: 789, test:456}); + done(); + }); + }); + it('returns the runtime settings - disableEditor true', function(done) { + info.init({disableEditor: true},{ + settings: { + getRuntimeSettings: function(opts) { + return Promise.resolve({ + a:1, + b:2 + }) + } + } + }); + request(app) + .get("/settings") + .expect(200) + .end(function(err,res) { + if (err) { + return done(err); + } + res.body.should.have.property("a",1); + res.body.should.have.property("b",2); + // no editorTheme if disabledEditor true + res.body.should.not.have.property("editorTheme"); + done(); + }); + }); + +}); diff --git a/test/unit/@node-red/editor-api/lib/editor/settings_spec.js b/test/unit/@node-red/editor-api/lib/editor/settings_spec.js index 8fcdbeba2..f8c690751 100644 --- a/test/unit/@node-red/editor-api/lib/editor/settings_spec.js +++ b/test/unit/@node-red/editor-api/lib/editor/settings_spec.js @@ -32,7 +32,6 @@ describe("api/editor/settings", function() { sinon.stub(theme,"settings",function() { return { existing: 123, test: 456 };}); app = express(); app.use(bodyParser.json()); - app.get("/settings",info.runtimeSettings); app.get("/settings/user",function(req,res,next) {req.user = "fred"; next()}, info.userSettings); app.post("/settings/user",function(req,res,next) {req.user = "fred"; next()},info.updateUserSettings); }); @@ -41,31 +40,6 @@ describe("api/editor/settings", function() { theme.settings.restore(); }); - it('returns the runtime settings', function(done) { - info.init({ - settings: { - getRuntimeSettings: function(opts) { - return Promise.resolve({ - a:1, - b:2, - editorTheme: { existing: 789 } - }) - } - } - }); - request(app) - .get("/settings") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("a",1); - res.body.should.have.property("b",2); - res.body.should.have.property("editorTheme",{existing: 789, test:456}); - done(); - }); - }); it('returns the user settings', function(done) { info.init({ settings: { diff --git a/test/unit/@node-red/runtime/lib/api/settings_spec.js b/test/unit/@node-red/runtime/lib/api/settings_spec.js index dbc5567da..3830e9ab2 100644 --- a/test/unit/@node-red/runtime/lib/api/settings_spec.js +++ b/test/unit/@node-red/runtime/lib/api/settings_spec.js @@ -101,7 +101,58 @@ describe("runtime-api/settings", function() { result.user.should.not.have.property("private"); }) }); + it("gets the filtered settings when editor disabled ", function() { + settings.init({ + settings: { + disableEditor: true, + foo: 123, + httpNodeRoot: "testHttpNodeRoot", + version: "testVersion", + paletteCategories :["red","blue","green"], + exportNodeSettings: (obj) => { + obj.testNodeSetting = "helloWorld"; + } + }, + nodes: { + listContextStores: () => { return {stores:["file","memory"], default: "file"} }, + paletteEditorEnabled: () => false, + getCredentialKeyType: () => "test-key-type" + }, + storage: { + projects: { + getActiveProject: () => 'test-active-project', + getFlowFilename: () => 'test-flow-file', + getCredentialsFilename: () => 'test-creds-file', + getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} + } + } + }) + return settings.getRuntimeSettings({ + user: { + username: "nick", + anonymous: false, + image: "http://example.com", + permissions: "*", + private: "secret" + } + }).then(result => { + result.should.have.property("user"); + result.user.should.have.property("username","nick"); + result.user.should.have.property("permissions","*"); + result.user.should.have.property("image","http://example.com"); + result.user.should.have.property("anonymous",false); + result.user.should.not.have.property("private"); + // Filtered out when disableEditor is true + result.should.not.have.property("paletteCategories",["red","blue","green"]); + result.should.not.have.property("testNodeSetting","helloWorld"); + result.should.not.have.property("foo",123); + result.should.not.have.property("flowEncryptionType","test-key-type"); + result.should.not.have.property("project"); + result.should.not.have.property("git"); + + }) + }); it('includes project settings if projects available', function() { settings.init({ settings: {