From 14c48253f6336e633f992c179c06d82421f7c4ad Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 24 Nov 2017 23:12:35 +0000 Subject: [PATCH] Confirm actions that would overwrite dirty workspace --- editor/js/main.js | 13 +- editor/js/ui/deploy.js | 41 ++--- editor/js/ui/notifications.js | 13 +- editor/js/ui/projects.js | 152 ++++++++++++------ editor/js/ui/tab-versionControl.js | 13 +- red/api/editor/projects/index.js | 1 - .../localfilesystem/projects/Project.js | 32 ++-- .../localfilesystem/projects/git/authCache.js | 42 +++++ .../projects/git/authServer.js | 28 ++-- .../storage/localfilesystem/projects/index.js | 32 ++-- 10 files changed, 244 insertions(+), 123 deletions(-) create mode 100644 red/runtime/storage/localfilesystem/projects/git/authCache.js diff --git a/editor/js/main.js b/editor/js/main.js index a9afd5bc1..da3d7af3e 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -59,13 +59,22 @@ // handled below return; } - if (notificationId === "project-change") { + if (notificationId === "project-update") { RED.nodes.clear(); RED.history.clear(); RED.view.redraw(true); RED.projects.refresh(function() { loadFlows(function() { - RED.notify("NLS: Project changed to "+msg.project); + console.log(msg); + var project = RED.projects.getActiveProject(); + var message = { + "change-branch":"Change to local branch '"+project.branches.local+"'", + "abort-merge":"Git merge aborted", + "loaded":"Project '"+msg.project+"' loaded", + "updated":"Project '"+msg.project+"' updated", + "pull":"Project '"+msg.project+"' reloaded" + }[msg.action] + RED.notify(message); RED.sidebar.info.refresh() }); }); diff --git a/editor/js/ui/deploy.js b/editor/js/ui/deploy.js index c22fa55c2..956742c56 100644 --- a/editor/js/ui/deploy.js +++ b/editor/js/ui/deploy.js @@ -224,24 +224,29 @@ RED.deploy = (function() { if (currentRev === null || deployInflight || currentRev === msg.revision) { return; } - var message = $('
'+RED._('deploy.confirm.backgroundUpdate')+ - '

'+ - ''+ - ''+ - '
'); - $(message.find('button')[0]).click(function(evt) { - evt.preventDefault(); - activeNotifyMessage.close(); - activeNotifyMessage = null; - }) - $(message.find('button')[1]).click(function(evt) { - evt.preventDefault(); - activeNotifyMessage.close(); - var nns = RED.nodes.createCompleteNodeSet(); - resolveConflict(nns,false); - activeNotifyMessage = null; - }) - activeNotifyMessage = RED.notify(message,null,true); + var message = $('
').text(RED._('deploy.confirm.backgroundUpdate')); + activeNotifyMessage = RED.notify(message,{ + fixed: true, + buttons: [ + { + text: RED._('deploy.confirm.button.ignore'), + click: function() { + activeNotifyMessage.close(); + activeNotifyMessage = null; + } + }, + { + text: RED._('deploy.confirm.button.review'), + class: "primary", + click: function() { + activeNotifyMessage.close(); + var nns = RED.nodes.createCompleteNodeSet(); + resolveConflict(nns,false); + activeNotifyMessage = null; + } + } + ] + }); } }); } diff --git a/editor/js/ui/notifications.js b/editor/js/ui/notifications.js index 3be2ffe6c..7a8da7ec5 100644 --- a/editor/js/ui/notifications.js +++ b/editor/js/ui/notifications.js @@ -18,7 +18,7 @@ RED.notify = (function() { var c = 0; return function(msg,type,fixed,timeout) { var options = {}; - if (typeof type === 'object') { + if (type !== null && typeof type === 'object') { options = type; fixed = options.fixed; timeout = options.timeout; @@ -56,6 +56,17 @@ RED.notify = (function() { } else { $(n).append(msg); } + if (options.buttons) { + var buttonSet = $('
').appendTo(n) + options.buttons.forEach(function(buttonDef) { + var b = $(''+ - ''+ - '
'+ ''); - $(message.find('button')[0]).click(function(evt) { - evt.preventDefault(); - notification.close(); - }) - $(message.find('button')[1]).click(function(evt) { - evt.preventDefault(); - var username = $('#projects-user-auth-username').val(); - var password = $('#projects-user-auth-password').val(); - body = body || {}; - var done = function(err) { - if (err) { - console.log("Failed to update auth"); - console.log(err); - } else { - sendRequest(options,body); - notification.close(); - } - - } - sendRequest({ - url: "projects/"+activeProject.name, - type: "PUT", - responses: { - 0: function(error) { - done(error,null); - }, - 200: function(data) { - done(null,data); - }, - 400: { - 'unexpected_error': function(error) { - done(error,null); - } - }, - } - },{ - remote: { - origin: { - username: username, - password: password - } - } - }); - - }) var notification = RED.notify(message,{ type:"error", fixed: true, - modal: true + modal: true, + buttons: [ + { + //id: "node-dialog-delete", + //class: 'leftButton', + text: RED._("common.label.cancel"), + click: function() { + notification.close(); + } + },{ + text: $(' Retry'), + click: function() { + var username = $('#projects-user-auth-username').val(); + var password = $('#projects-user-auth-password').val(); + body = body || {}; + var done = function(err) { + if (err) { + console.log("Failed to update auth"); + console.log(err); + } else { + sendRequest(options,body); + notification.close(); + } + + } + sendRequest({ + url: "projects/"+activeProject.name, + type: "PUT", + responses: { + 0: function(error) { + done(error,null); + }, + 200: function(data) { + done(null,data); + }, + 400: { + 'unexpected_error': function(error) { + done(error,null); + } + }, + } + },{ + remote: { + origin: { + username: username, + password: password + } + } + }); + } + } + ] }); return; } else if (responses[xhr.responseJSON.error]) { @@ -888,7 +937,6 @@ RED.projects = (function() { $.getJSON("projects",function(data) { if (data.active) { $.getJSON("projects/"+data.active, function(project) { - console.log(project.branches); activeProject = project; // updateProjectSummary(); // updateProjectDescription(); diff --git a/editor/js/ui/tab-versionControl.js b/editor/js/ui/tab-versionControl.js index c506ba9a4..96c0d76bf 100644 --- a/editor/js/ui/tab-versionControl.js +++ b/editor/js/ui/tab-versionControl.js @@ -582,9 +582,14 @@ RED.sidebar.versionControl = (function() { } var spinner = utils.addSpinnerOverlay(localBranchBox); var activeProject = RED.projects.getActiveProject(); + RED.deploy.setDeployInflight(true); utils.sendRequest({ url: "projects/"+activeProject.name+"/branches", type: "POST", + requireCleanWorkspace: true, + cancel: function() { + spinner.remove(); + }, responses: { 0: function(error) { spinner.remove(); @@ -606,7 +611,10 @@ RED.sidebar.versionControl = (function() { } }, } - },body); + },body).always(function(){ + console.log("switch deployinflight to false") + RED.deploy.setDeployInflight(false); + }); } }); @@ -679,6 +687,9 @@ RED.sidebar.versionControl = (function() { refresh(true); }, 400: { + 'git_connection_failed': function(error) { + RED.notify(error.message); + }, 'unexpected_error': function(error) { console.log(error); // done(error,null); diff --git a/red/api/editor/projects/index.js b/red/api/editor/projects/index.js index 78002146a..59d9b3691 100644 --- a/red/api/editor/projects/index.js +++ b/red/api/editor/projects/index.js @@ -371,7 +371,6 @@ module.exports = { res.json(data); }) .catch(function(err) { - console.log(err.stack); if (err.code) { res.status(400).json({error:err.code, message: err.message}); } else { diff --git a/red/runtime/storage/localfilesystem/projects/Project.js b/red/runtime/storage/localfilesystem/projects/Project.js index dbdfb975f..eea9035ca 100644 --- a/red/runtime/storage/localfilesystem/projects/Project.js +++ b/red/runtime/storage/localfilesystem/projects/Project.js @@ -29,14 +29,8 @@ var log; var projectsDir; -var authCache = {}; +var authCache = require("./git/authCache"); -function getAuth(project,remote) { - if (authCache.hasOwnProperty(project) && authCache[project].hasOwnProperty(remote)) { - return authCache[project][remote]; - } - return null; -} function Project(name) { this.name = name; this.path = fspath.join(projectsDir,name); @@ -179,10 +173,9 @@ Project.prototype.update = function (data) { this.package.description = data.summary; } if (data.hasOwnProperty('remote')) { - authCache[project.name] = authCache[project.name]||{}; for (var remote in data.remote) { if (data.remote.hasOwnProperty(remote)) { - authCache[project.name][remote] = data.remote[remote]; + authCache.set(project.name,remote,data.remote[remote]); } } } @@ -255,7 +248,7 @@ Project.prototype.status = function() { var fetchPromise; if (this.remote) { - fetchPromise = gitTools.fetch(this.path,getAuth(this.name,'origin')); + fetchPromise = gitTools.fetch(this.path,authCache.get(this.name,'origin')); } else { fetchPromise = when.resolve(); } @@ -281,9 +274,7 @@ Project.prototype.status = function() { }); } return fetchPromise.then(completeStatus).catch(function(e) { - if (e.code === 'git_auth_failed') { - console.log("Fetch auth failed"); - } else { + if (e.code !== 'git_auth_failed') { console.log("Fetch failed"); console.log(e); } @@ -292,17 +283,17 @@ Project.prototype.status = function() { }; Project.prototype.push = function (remoteBranchName,setRemote) { - return gitTools.push(this.path, remoteBranchName, setRemote, getAuth(this.name,'origin')); + return gitTools.push(this.path, remoteBranchName, setRemote, authCache.get(this.name,'origin')); }; Project.prototype.pull = function (remoteBranchName,setRemote) { var self = this; if (setRemote) { return gitTools.setUpstream(this.path, remoteBranchName).then(function() { - return gitTools.pull(self.path, null, getAuth(self.name,'origin')); + return gitTools.pull(self.path, null, authCache.get(self.name,'origin')); }) } else { - return gitTools.pull(this.path, remoteBranchName, getAuth(this.name,'origin')); + return gitTools.pull(this.path, remoteBranchName, authCache.get(this.name,'origin')); } }; @@ -355,7 +346,7 @@ Project.prototype.getBranches = function (remote) { var self = this; var fetchPromise; if (remote) { - fetchPromise = gitTools.fetch(this.path,getAuth(this.name,'origin')) + fetchPromise = gitTools.fetch(this.path,authCache.get(this.name,'origin')) } else { fetchPromise = when.resolve(); } @@ -535,13 +526,12 @@ function createProject(metadata) { if (metadata.remote) { var auth; if (metadata.remote.hasOwnProperty("username") && metadata.remote.hasOwnProperty("password")) { - authCache[project] = { - origin: { // TODO: hardcoded remote name + authCache.set(project,'origin',{ // TODO: hardcoded remote name username: metadata.remote.username, password: metadata.remote.password } - } - auth = authCache[project].origin; + ); + auth = authCache.get(project,'origin'); } return gitTools.clone(metadata.remote,auth,projectPath).then(function(result) { diff --git a/red/runtime/storage/localfilesystem/projects/git/authCache.js b/red/runtime/storage/localfilesystem/projects/git/authCache.js new file mode 100644 index 000000000..40f0ba9c5 --- /dev/null +++ b/red/runtime/storage/localfilesystem/projects/git/authCache.js @@ -0,0 +1,42 @@ +/** + * 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 authCache = {} + +module.exports = { + clear: function(project,remote) { + if (remote && authCache.hasOwnProperty(project)) { + delete authCache[project][remote]; + return; + } + delete authCache[project]; + }, + set: function(project,remote,auth) { + if (authCache.hasOwnProperty(project)) { + authCache[project][remote] = auth; + } else { + authCache[project] = { + remote: auth + } + } + }, + get: function(project,remote) { + if (authCache.hasOwnProperty(project)) { + return authCache[project][remote]; + } + return + } +} diff --git a/red/runtime/storage/localfilesystem/projects/git/authServer.js b/red/runtime/storage/localfilesystem/projects/git/authServer.js index 0a2ae60e4..fc94edfaf 100644 --- a/red/runtime/storage/localfilesystem/projects/git/authServer.js +++ b/red/runtime/storage/localfilesystem/projects/git/authServer.js @@ -1,18 +1,18 @@ /** -* 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. -**/ + * 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 net = require("net"); var fs = require("fs-extra"); diff --git a/red/runtime/storage/localfilesystem/projects/index.js b/red/runtime/storage/localfilesystem/projects/index.js index 9854e834d..5e0eedbb8 100644 --- a/red/runtime/storage/localfilesystem/projects/index.js +++ b/red/runtime/storage/localfilesystem/projects/index.js @@ -147,7 +147,7 @@ function getFileDiff(project,file,type) { } function getCommits(project,options) { checkActiveProject(project); - return activeProject.getCommits(options); + return activeProject.getCommits(options); } function getCommit(project,sha) { checkActiveProject(project); @@ -164,7 +164,9 @@ function push(project,remoteBranchName,setRemote) { } function pull(project,remoteBranchName,setRemote) { checkActiveProject(project); - return activeProject.pull(remoteBranchName,setRemote).then(reloadActiveProject); + return activeProject.pull(remoteBranchName,setRemote).then(function() { + return reloadActiveProject("pull"); + }); } function getStatus(project) { checkActiveProject(project); @@ -176,7 +178,9 @@ function resolveMerge(project,file,resolution) { } function abortMerge(project) { checkActiveProject(project); - return activeProject.abortMerge().then(reloadActiveProject); + return activeProject.abortMerge().then(function() { + return reloadActiveProject("abort-merge") + }); } function getBranches(project,remote) { checkActiveProject(project); @@ -184,7 +188,9 @@ function getBranches(project,remote) { } function setBranch(project,branchName,isCreate) { checkActiveProject(project); - return activeProject.setBranch(branchName,isCreate).then(reloadActiveProject); + return activeProject.setBranch(branchName,isCreate).then(function() { + return reloadActiveProject("change-branch"); + }); } function getBranchStatus(project,branchName) { checkActiveProject(project); @@ -194,14 +200,14 @@ function getActiveProject() { return activeProject; } -function reloadActiveProject() { +function reloadActiveProject(action) { return runtime.nodes.stopFlows().then(function() { return runtime.nodes.loadFlows(true).then(function() { - runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: activeProject.name}}); + runtime.events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}}); }).catch(function(err) { // We're committed to the project change now, so notify editors // that it has changed. - runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: activeProject.name}}); + runtime.events.emit("runtime-event",{id:"project-update", payload:{ project: activeProject.name, action:action}}); throw err; }); }); @@ -224,7 +230,7 @@ function setActiveProject(projectName) { // console.log("Updated file targets to"); // console.log(flowsFullPath) // console.log(credentialsFile) - return reloadActiveProject(); + return reloadActiveProject("loaded"); }) }); } @@ -244,7 +250,7 @@ function updateProject(project,data) { flowsFileBackup = activeProject.getFlowFileBackup(); credentialsFile = activeProject.getCredentialsFile(); credentialsFileBackup = activeProject.getCredentialsFileBackup(); - return reloadActiveProject(); + return reloadActiveProject("updated"); } else if (result.credentialSecretChanged) { if (isReset || !wasInvalid) { if (isReset) { @@ -255,11 +261,11 @@ function updateProject(project,data) { .then(runtime.storage.saveCredentials) .then(function() { if (wasInvalid) { - return reloadActiveProject(); + return reloadActiveProject("updated"); } }); } else if (wasInvalid) { - return reloadActiveProject(); + return reloadActiveProject("updated"); } } }); @@ -277,11 +283,11 @@ function setCredentialSecret(data) { //existingSecret,secret) { .then(runtime.storage.saveCredentials) .then(function() { if (wasInvalid) { - return reloadActiveProject(); + return reloadActiveProject("updated"); } }); } else if (wasInvalid) { - return reloadActiveProject(); + return reloadActiveProject("updated"); } }) }