http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js index bd2944b..ed743f4 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/flow-designer.js @@ -20,17 +20,45 @@ import {Workflow} from '../domain/workflow'; import Constants from '../utils/constants'; import {WorkflowGenerator} from '../domain/workflow-xml-generator'; import {WorkflowImporter} from '../domain/workflow-importer'; +import {WorkflowJsonImporter} from '../domain/workflow-json-importer'; import {WorkflowContext} from '../domain/workflow-context'; -import {DefaultLayoutManager as LayoutManager} from '../domain/default-layout-manager'; -import EmberValidations,{ validator } from 'ember-validations'; +import {JSPlumbRenderer} from '../domain/jsplumb-flow-renderer'; +import {CytoscapeRenderer} from '../domain/cytoscape-flow-renderer'; +import {FindNodeMixin} from '../domain/findnode-mixin'; +import { validator, buildValidations } from 'ember-cp-validations'; +import WorkflowPathUtil from '../domain/workflow-path-util'; +const Validations = buildValidations({ + 'dataNodes': { /* For Cytoscape */ + validators: [ + validator('duplicate-data-node-name', { + dependentKeys: ['[email protected]'] + }) + ] + }, + 'workflow.killNodes': { + validators: [ + validator('duplicate-kill-node-name', { + dependentKeys: ['[email protected]'] + }) + ] + }, + 'flattenedNodes': { + validators: [ + validator('duplicate-flattened-node-name', { + dependentKeys: ['[email protected]'] + }) + ] + } +}); -export default Ember.Component.extend(EmberValidations,{ +export default Ember.Component.extend(FindNodeMixin, Validations, { workflowContext : WorkflowContext.create({}), workflowTitle:"", previewXml:"", supportedActionTypes:["java", "hive", "pig", "sqoop", "shell", "spark", "map-reduce", "hive2", "sub-workflow", "distcp", "ssh", "FS"], workflow:null, + hoveredWidget:null,/**/ showingConfirmationNewWorkflow:false, showingWorkflowConfigProps:false, workflowSubmitConfigs:{}, @@ -40,106 +68,118 @@ export default Ember.Component.extend(EmberValidations,{ domain:{}, showActionEditor : false, flattenedNodes: [], - + dataNodes: [], /* For cytoscape */ + hoveredAction: null, workflowImporter:WorkflowImporter.create({}), - designerPlumb:null, propertyExtractor : Ember.inject.service('property-extractor'), + clipboardService : Ember.inject.service('workflow-clipboard'), + workspaceManager : Ember.inject.service('workspace-manager'), showGlobalConfig : false, showParameterSettings : false, + showNotificationPanel : false, globalConfig : {}, parameters : {}, clonedDomain : {}, clonedErrorNode : {}, validationErrors : [], - layoutManager:null, showingFileBrowser : false, killNode : {}, isWorkflowImporting: false, isImportingSuccess: true, - initialize :function(){ - this.designerPlumb=jsPlumb.getInstance({}); - this.layoutManager=LayoutManager.create({}); + shouldPersist : false, + useCytoscape: Constants.useCytoscape, + cyOverflow: {}, + clipboard : Ember.computed.alias('clipboardService.clipboard'), + isStackTraceVisible: false, + isStackTraceAvailable: false, + stackTrace:"", + initialize : function(){ + var id = 'cy-' + Math.ceil(Math.random() * 1000); + this.set('cyId', id); + this.sendAction('register', this.get('tabInfo'), this); + }.on('init'), + elementsInserted :function(){ + if (this.useCytoscape){ + this.flowRenderer=CytoscapeRenderer.create({id : this.get('cyId')}); + }else{ + this.flowRenderer=JSPlumbRenderer.create({}); + } + this.setConentWidth(); this.set('workflow',Workflow.create({})); if(this.get("xmlAppPath")){ - var workflowXmlPath = this.get("xmlAppPath"), relXmlPath = "", tempArr; - if(workflowXmlPath.indexOf("://") === -1 && workflowXmlPath.indexOf(":") === -1){ - relXmlPath = workflowXmlPath; - } else{ - tempArr = workflowXmlPath.split("//")[1].split("/"); - tempArr.splice(0, 1); - relXmlPath = "/" + tempArr.join("/"); - if(!(relXmlPath.indexOf(".xml") === relXmlPath.length-4)) { - if(relXmlPath.charAt(relXmlPath.length-1) !== "/"){ - relXmlPath = relXmlPath+ "/" +"workflow.xml"; - } else{ - relXmlPath = relXmlPath+"workflow.xml"; - } - } - } - this.importWorkflow(relXmlPath); + this.showExistingWorkflow(); return; - }else{ + } else { this.workflow.initialize(); this.initAndRenderWorkflow(); this.$('#wf_title').focus(); - this.restoreWorkinProgress(); + if (Constants.autoRestoreWorkflowEnabled){ + this.restoreWorkflow(); + } + } + if(Ember.isBlank(this.get('workflow.name'))){ + this.set('workflow.name', Ember.copy(this.get('tabInfo.name'))); } - }.on('didInsertElement'), - validations: { - 'flattenedNodes': { - inline : validator(function() { - var nodeNames = new Map(); - this.get("validationErrors").clear(); - this.get('flattenedNodes').forEach((item)=>{ - Ember.set(item, "errors", false); - if(nodeNames.get(item.name)){ - Ember.set(item, "errors", true); - this.get("validationErrors").pushObject({node:item,message:"Node name should be unique"}); - }else{ - nodeNames.set(item.name, item); - Ember.set(item, "errors", false); - } - if(this.get("supportedActionTypes").indexOf(item.actionType) === -1 && item.type === "action"){ - this.get('validationErrors').pushObject({node : item ,message : item.actionType+" is unsupported"}); - } - var nodeErrors=item.validateCustom(); - if (nodeErrors.length>0){ - Ember.set(item, "errors", true); - nodeErrors.forEach(function(errMsg){ - this.get("errors").pushObject({node:item,message:errMsg }); - }.bind(this)); - } - }.bind(this)); - if(this.get('flattenedNodes').length !== nodeNames.size || this.get("errors").length>0){ - return true; - } - }) - }, - "workflow.killnodes": { - inline : validator(function() { - let killNodes = [], flag; - if(this.get("workflow") && this.get("workflow").killNodes){ - killNodes = this.get("workflow").killNodes; - for(let i=0; i<killNodes.length; i++){ - for(let j=0; j<killNodes.length; j++){ - if(killNodes[i].name === killNodes[j].name && i !== j){ - this.get('validationErrors').pushObject({node : killNodes[j] ,message : "Duplicate killnode"}); - flag = true; - break; - } - } - if(flag){ - break; - } - } - } - if (flag){ - return true; + }.on('didInsertElement'), + restoreWorkflow(){ + if (!this.get("isNew")){ + var draftWorkflow=this.getDraftWorkflow(); + if (draftWorkflow){ + this.resetDesigner(); + this.set("workflow",draftWorkflow); + this.rerender(); + this.doValidation(); + } + } + }, + observeXmlAppPath : Ember.observer('xmlAppPath', function(){ + if(!this.get('xmlAppPath') || null === this.get('xmlAppPath')){ + return; + }else{ + this.showExistingWorkflow(); + } + }), + observeFilePath : Ember.observer('workflowFilePath', function(){ + if(!this.get('workflowFilePath') || null === this.get('workflowFilePath')){ + return; + }else{ + this.sendAction('changeFilePath', this.get('tabInfo'), this.get('workflowFilePath')); + } + }), + nameObserver : Ember.observer('workflow.name', function(){ + if(!this.get('workflow')){ + return; + }else if(this.get('workflow') && Ember.isBlank(this.get('workflow.name'))){ + if(!this.get('clonedTabInfo')){ + this.set('clonedTabInfo', Ember.copy(this.get('tabInfo'))); + } + this.sendAction('changeTabName', this.get('tabInfo'), this.get('clonedTabInfo.name')); + }else{ + this.sendAction('changeTabName', this.get('tabInfo'), this.get('workflow.name')); + } + }), + showParentWorkflow(type, path){ + this.sendAction('openTab', type, path); + }, + showExistingWorkflow(){ + var workflowXmlPath = this.get("xmlAppPath"), relXmlPath = "", tempArr; + if(workflowXmlPath.indexOf("://") === -1 && workflowXmlPath.indexOf(":") === -1){ + relXmlPath = workflowXmlPath; + } else{ + tempArr = workflowXmlPath.split("//")[1].split("/"); + tempArr.splice(0, 1); + relXmlPath = "/" + tempArr.join("/"); + if(relXmlPath.indexOf(".xml") !== relXmlPath.length-4) { + if(relXmlPath.charAt(relXmlPath.length-1) !== "/"){ + relXmlPath = relXmlPath+ "/" +"workflow.xml"; + } else{ + relXmlPath = relXmlPath+"workflow.xml"; } - }) + } } + this.importWorkflow(relXmlPath); }, setConentWidth(){ var offset = 120; @@ -150,197 +190,101 @@ export default Ember.Component.extend(EmberValidations,{ return; }); }, + workflowXmlDownload(workflowXml){ + var link = document.createElement("a"); + link.download = "workflow.xml"; + link.href = "data:text/xml,"+vkbeautify.xml(workflowXml); + link.click(); + }, nodeRendered: function(){ - var self=this; + this.doValidation(); if(this.get('renderNodeTransitions')){ - var connections=[]; - var visitedNodes=[]; - this.renderTransitions(this.get("workflow").startNode,connections,visitedNodes); - this.workflowConnections=connections; + this.flowRenderer.onDidUpdate(this,this.get("workflow").startNode,this.get("workflow")); this.layout(); - this.designerPlumb.setSuspendDrawing(true); - this.designerPlumb.batch(function(){ - connections.forEach(function(conn){ - self.designerPlumb.connect(conn); - }); - }); - this.designerPlumb.setSuspendDrawing(false,true); this.set('renderNodeTransitions',false); } + this.resize(); this.persistWorkInProgress(); }.on('didUpdate'), - cleanUpJsplumb:function(){ - this.get('flattenedNodes').clear(); + resize(){ + this.flowRenderer.resize(); + }, + cleanupFlowRenderer:function(){ this.set('renderNodeTransitions',false); - this.designerPlumb.detachEveryConnection(); + this.flowRenderer.cleanup(); }.on('willDestroyElement'), initAndRenderWorkflow(){ - this.designerPlumb.ready(function() { + var panelOffset=this.$(".designer-panel").offset(); + var canvasHeight=Ember.$(window).height()-panelOffset.top-25; + this.flowRenderer.initRenderer(function(){ this.renderWorkflow(); - }.bind(this)); + }.bind(this),{context:this,flattenedNodes:this.get("flattenedNodes"),dataNodes:this.get("dataNodes"), cyOverflow:this.get("cyOverflow"),canvasHeight:canvasHeight}); }, renderWorkflow(){ - this.get('flattenedNodes').clear(); this.set('renderNodeTransitions', true); - var visitedNodes=[]; - this.renderNodes(this.get("workflow").startNode,visitedNodes); + this.flowRenderer.renderWorkflow(this.get("workflow")); + this.doValidation(); }, rerender(){ - this.designerPlumb.detachEveryConnection(); + this.flowRenderer.cleanup(); this.renderWorkflow(this.get("workflow")); }, setCurrentTransition(transition){ this.set("currentTransition",transition); }, - renderNodes(node,visitedNodes){ - if (!node || node.isKillNode()){ - return; - } - if (visitedNodes.contains(node)){ - return; - } - visitedNodes.push(node); - if(!this.get('flattenedNodes').contains(node)){ - this.get('flattenedNodes').pushObject(node); - } - if (node.transitions.length > 0){ - node.transitions.forEach(function(transition) { - var target = transition.targetNode; - this.renderNodes(target,visitedNodes); - }.bind(this)); - } - }, - createConnection(sourceNode,target,transition){ - var connectionColor="#777"; - var lineWidth=1; - if (transition.condition){ - if(transition.condition==="default"){ - lineWidth=2; - }else if (transition.condition==="error"|| transition.errorPath){ - connectionColor=Constants.globalSetting.errorTransitionColor; - } - } - var connectionObj={ - source:sourceNode.id, - target:target.id, - connector:["Straight"], - paintStyle:{lineWidth:lineWidth,strokeStyle:connectionColor}, - endpointStyle:{fillStyle:'rgb(243,229,0)'}, - endpoint: ["Dot", { - radius: 1 - }], - alwaysRespectStubs:true, - anchors: [["Bottom"],["Top"]], - overlays:[] - }; - return connectionObj; + actionInfo(node){ + this.send("showNotification", node); }, deleteTransition(transition){ + this.createSnapshot(); this.get("workflow").deleteTransition(transition); + this.showUndo('transition'); this.rerender(); }, - renderTransitions(sourceNode,connections,visitedNodes){ + showWorkflowActionSelect(element){ var self=this; - if(!sourceNode){ - return; - } - if (visitedNodes.contains(sourceNode)){ - return; - } - if (sourceNode.hasTransition() ){ - var transitionCount=sourceNode.transitions.length; - sourceNode.transitions.forEach(function(transition) { - var target = transition.targetNode; - if (target.isKillNode() || !Constants.showErrorTransitions && transition.isOnError()){ - return; - } - var connectionObj=self.createConnection(sourceNode,target,transition); - - if (transition.condition){ - var conditionHTML = "<div class='decision-condition' title='"+transition.condition+"'>"+ transition.condition+"</div>"; - connectionObj.overlays.push([ "Label", {label:conditionHTML, location:0.75, id:"myLabel" } ]); - } - if (!target.isPlaceholder()){ - connectionObj.overlays.push(["PlainArrow",{location:-0.1,width: 7,length: 7}]); - } - if (!(sourceNode.isPlaceholder() || target.isKillNode())){ - var location=target.type==="placeholder"?1:0.5; - var addNodeoverlay=["Custom" , { - id: sourceNode.id+"_"+target.id+"_"+"connector", - location:location, - create:function(component) { - var container=Ember.$('<div />'); - var plus= Ember.$('<div class="fa fa-plus connector_overlay_new"></div>'); - if ((sourceNode.isDecisionNode() && transitionCount>1 ||sourceNode.isForkNode() && transitionCount>2 ) && - target.isPlaceholder() && - !transition.isDefaultCasePath()){ - var trash=Ember.$('<div class="node_actions node_left"><i class="fa fa-trash-o"></i></div>'); - trash.on("click",function(){ - self.deleteTransition(transition); - }); - plus.append(trash); - } - container.append(plus); - return container; - }, - events:{ - click:function(labelOverlay, originalEvent) { - var element = originalEvent.target; - self.set('popOverElement', element); - self.setCurrentTransition(transition); - self.$('.popover').popover('destroy'); - Ember.$(element).parents(".jsplumb-overlay").css("z-index", "4"); - self.$(element).attr('data-toggle','popover'); - self.$(element).popover({ - html : true, - title : "Add Node <button type='button' class='close'>×</button>", - placement: 'right', - trigger : 'focus', - content : function(){ - return self.$('#workflow-actions').html(); - } - }); - self.$(element).popover("show"); - self.$('.popover .close').on('click',function(){ - Ember.$(".jsplumb-overlay").css("z-index", ""); - self.$('.popover').popover('destroy'); - }); - } - } - }]; - connectionObj.overlays.push(addNodeoverlay); - } - connections.push(connectionObj); - self.renderTransitions(target,connections,visitedNodes); - }); - } + this.$('.popover').popover('destroy'); + Ember.$(element).parents(".jsplumb-overlay").css("z-index", "4"); + this.$(element).attr('data-toggle','popover'); + this.$(element).popover({ + html : true, + title : "Add Node <button type='button' class='close'>×</button>", + placement: 'right', + trigger : 'focus', + content : function(){ + return self.$('#workflow-actions').html(); + } + }); + this.$(element).popover("show"); + this.$('.popover .close').on('click',function(){ + Ember.$(".jsplumb-overlay").css("z-index", ""); + this.$('.popover').popover('destroy'); + }.bind(this)); }, + layout(){ - var nodes = Ember.$(".nodecontainer"); - //var edges = this.designerPlumb.getConnections(); - var edges=this.workflowConnections; - this.layoutManager.doLayout(this,nodes,edges,this.get("workflow")); - this.designerPlumb.repaintEverything(); - var endNodeTop=this.$("#node-end").offset().top; - var endNodeLeft=this.$("#node-end").offset().left; - this.$("#killnodes-container").offset({top:endNodeTop+50,left:endNodeLeft-50}); - var top = this.$("#killnodes-container").offset().top + 40; - var left = this.$("#killnodes-container").offset().left - 28; - this.$('.kill').each(function(index,value){ - this.$(value).offset({top:top,left:left}); - top = this.$(value).offset().top+70 ; - }.bind(this)); + this.flowRenderer.refresh(); }, doValidation(){ - this.set('validationErrors',[]); - this.validate().then(() => { - this.set('validationErrors',[]); - }).catch(() => { - this.get('flattenedNodes').filterBy('errors',true).forEach((node)=>{ - this.get('validationErrors').pushObjects(node.errorMsgs); - }.bind(this)); - - }.bind(this)); + this.validate(); + }, + getStackTrace(data){ + if(data){ + try{ + var stackTraceMsg = JSON.parse(data).stackTrace; + if(!stackTraceMsg){ + return ""; + } + if(stackTraceMsg instanceof Array){ + return stackTraceMsg.join("").replace(/\tat /g, ' at '); + } else { + return stackTraceMsg.replace(/\tat /g, '<br/> at '); + } + } catch(err){ + return ""; + } + } + return ""; }, importWorkflow(filePath){ var self = this; @@ -352,17 +296,34 @@ export default Ember.Component.extend(EmberValidations,{ workflowXmlDefered.promise.then(function(data){ this.importWorkflowFromString(data); this.set("isWorkflowImporting", false); - }.bind(this)).catch(function(e){ + }.bind(this)).catch(function(data){ + var stackTraceMsg = self.getStackTrace(data.responseText); + if(stackTraceMsg.length){ + self.set("isStackTraceVisible", true); + self.set("stackTrace", stackTraceMsg); + self.set("isStackTraceAvailable", true); + } else { + self.set("isStackTraceVisible", false); + self.set("isStackTraceAvailable", false); + } self.set("isWorkflowImporting", false); self.set("isImportingSuccess", false); }); }, importWorkflowFromString(data){ var workflow=this.get("workflowImporter").importWorkflow(data); - this.resetDesigner(); - this.set("workflow",workflow); - this.rerender(); - this.doValidation(); + if(this.get('workflow')){ + this.resetDesigner(); + this.set("workflow",workflow); + this.initAndRenderWorkflow(); + this.rerender(); + this.doValidation(); + }else{ + this.workflow.initialize(); + this.set("workflow",workflow); + this.initAndRenderWorkflow(); + this.$('#wf_title').focus(); + } }, getWorkflowFromHdfs(filePath){ var url = Ember.ENV.API_URL + "/readWorkflowXml?workflowXmlPath="+filePath; @@ -377,16 +338,16 @@ export default Ember.Component.extend(EmberValidations,{ } }).done(function(data){ deferred.resolve(data); - }).fail(function(){ - deferred.reject(); + }).fail(function(data){ + deferred.reject(data); }); return deferred; }, resetDesigner(){ this.set("isImportingSuccess", true); - this.set("xmlAppPath", null) - this.set('errors',{}); - this.set('validationErrors',{}); + this.set("xmlAppPath", null); + this.set('errors',[]); + this.set('validationErrors',[]); this.set('workflowFilePath',""); this.get("workflow").resetWorfklow(); this.set('globalConfig', {}); @@ -395,7 +356,7 @@ export default Ember.Component.extend(EmberValidations,{ this.set('workflow.parameters', {}); } this.set('parameters', {}); - this.designerPlumb.reset(); + this.flowRenderer.reset(); }, resetZoomLevel(){ this.set("zoomLevel", 1); @@ -423,33 +384,150 @@ export default Ember.Component.extend(EmberValidations,{ return deferred; }, persistWorkInProgress(){ - //TODO later + var json=JSON.stringify(this.get("workflow")); + this.get('workspaceManager').saveWorkInProgress(this.get('tabInfo.id'), json); + }, + getDraftWorkflow(){ + var drafWorkflowJson = this.get('workspaceManager').restoreWorkInProgress(this.get('tabInfo.id')); + var workflowImporter=WorkflowJsonImporter.create({}); + var workflow=workflowImporter.importWorkflow(drafWorkflowJson); + return workflow; + }, + createSnapshot() { + this.set('undoAvailable', false); + this.set('workflowSnapshot', JSON.stringify(this.get("workflow"))); + }, + showUndo (type){ + this.set('undoAvailable', true); + this.set('undoType', type); + }, + deleteWorkflowNode(node){ + this.createSnapshot(); + if(node.isKillNode()){ + var result=this.get("workflow").deleteKillNode(node); + if (result && result.status===false){ + this.get('validationErrors').pushObject({node : node ,message :result.message}); + } + } else { + this.get("workflow").deleteNode(node); + } + this.rerender(); + this.doValidation(); + this.showUndo('node'); + }, + addWorkflowBranch(node){ + this.createSnapshot(); + this.get("workflow").addBranch(node); + this.rerender(); + }, + openWorkflowEditor(node){ + this.createSnapshot(); + var validOkToNodes = WorkflowPathUtil.findValidTransitionsTo(this.get('workflow'), node); + this.set('showActionEditor', true); + this.set('currentAction', node.actionType); + var domain = node.getNodeDetail(); + this.set('clonedDomain',Ember.copy(domain)); + this.set('clonedErrorNode', node.errorNode); + this.set('clonedKillMessage',node.get('killMessage')); + node.set("domain", domain); + node.set("validOkToNodes", validOkToNodes); + this.set('currentNode', node); + }, + openDecisionEditor(node) { + this.get("addBranchListener").trigger("showBranchOptions", node); + }, + + copyNode(node){ + this.get('clipboardService').setContent(node, 'copy'); + }, + cutNode(node){ + this.get('clipboardService').setContent(node, 'cut'); + this.deleteWorkflowNode(node); + }, + replaceNode(node){ + var clipboardContent = this.get('clipboardService').getContent(); + Ember.set(node, 'name', clipboardContent.name+'-copy'); + Ember.set(node, 'domain', clipboardContent.domain); + Ember.set(node, 'actionType', clipboardContent.actionType); + this.rerender(); + this.doValidation(); }, - restoreWorkinProgress(){ - //TODO later + scrollToNewPosition(){ + if (Constants.useCytoscape){ + return; + } + var scroll = Ember.$(window).scrollTop(); + Ember.$('html, body') + .animate({ + scrollTop: scroll+200 + }, 1000); + }, + openSaveWorkflow (){ + this.get('workflowContext').clearErrors(); + var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"), + workflowContext:this.get('workflowContext')}); + var workflowXml=workflowGenerator.process(); + if(this.get('workflowContext').hasErrors()){ + this.set('errors',this.get('workflowContext').getErrors()); + }else{ + var dynamicProperties = this.get('propertyExtractor').getDynamicProperties(workflowXml); + var configForSubmit={props:dynamicProperties,xml:workflowXml,params:this.get('workflow.parameters')}; + this.set("workflowSubmitConfigs",configForSubmit); + this.set("showingSaveWorkflow",true); + } + }, + openJobConfig (){ + this.get('workflowContext').clearErrors(); + var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"), + workflowContext:this.get('workflowContext')}); + var workflowXml=workflowGenerator.process(); + if(this.get('workflowContext').hasErrors()){ + this.set('errors',this.get('workflowContext').getErrors()); + }else{ + var dynamicProperties = this.get('propertyExtractor').getDynamicProperties(workflowXml); + var configForSubmit={props:dynamicProperties,xml:workflowXml,params:this.get('workflow.parameters')}; + this.set("workflowSubmitConfigs",configForSubmit); + this.set("showingWorkflowConfigProps",true); + } }, actions:{ + showStackTrace(){ + this.set("isStackTraceVisible", true); + }, + hideStackTrace(){ + this.set("isStackTraceVisible", false); + }, showWorkflowSla (value) { this.set('showWorkflowSla', value); }, showCreateKillNode (value){ - this.set('showCreateKillNode', value); + this.set('showKillNodeManager', value); + this.set('addKillNodeMode', true); + this.set('editMode', false); + }, + showKillNodeManager (value){ + this.set('showKillNodeManager', value); + this.set('addKillNodeMode', false); + }, + closeKillNodeManager(){ + this.set("showKillNodeManager", false); }, showVersionSettings(value){ this.set('showVersionSettings', value); }, - showParameterSettings(value){ + showingParameterSettings(value){ if(this.get('workflow.parameters') !== null){ this.set('parameters', Ember.copy(this.get('workflow.parameters'))); }else{ - this.set('globalConfig', {}); + this.set('parameters', {}); } this.set('showParameterSettings', value); }, showCredentials(value){ this.set('showCredentials', value); }, - createKillNode(){ + createKillNode(killNode){ + this.set("killNode", killNode); this.set("createKillnodeError",null); var existingKillNode=this.get('workflow').get("killNodes").findBy("name",this.get('killNode.name')); if (existingKillNode){ @@ -460,7 +538,7 @@ export default Ember.Component.extend(EmberValidations,{ this.set("createKillnodeError","The kill node cannot be empty"); return; } - this.get("workflow").createKillNode(this.get('killNode.name'),this.get('killNode.message')); + this.get("workflow").createKillNode(this.get('killNode.name'),this.get('killNode.killMessage')); this.set('killNode',{}); this.rerender(); this.layout(); @@ -469,62 +547,89 @@ export default Ember.Component.extend(EmberValidations,{ this.set('showCreateKillNode', false); }, addNode(type){ + this.createSnapshot(); var currentTransition=this.get("currentTransition"); - var newNode=this.get("workflow").addNode(currentTransition,type); - if(currentTransition.targetNode.isPlaceholder()){ - this.designerPlumb.remove(currentTransition.targetNode.id); - } + this.get("workflow").addNode(this.findTransition(this.get("workflow").startNode, currentTransition.sourceNodeId, currentTransition.targetNode.id),type); this.rerender(); this.doValidation(); - var scroll = $(window).scrollTop(); - Ember.$('html, body') - .animate({ - scrollTop: scroll+200 - }, 1000); + this.scrollToNewPosition(); }, + nameChanged(){ this.doValidation(); }, - deleteNode(node){ - if(node.isKillNode()){ - var result=this.get("workflow").deleteKillNode(node); - if (result && result.status===false){ - this.get('validationErrors').pushObject({node : node ,message :result.message}); - } - } else { - this.get("workflow").deleteNode(node); + copyNode(node){ + this.copyNode(node); + }, + pasteNode(){ + var clipboardContent = this.get('clipboardService').getContent(); + var currentTransition = this.get("currentTransition"); + var node = this.get("workflow").addNode(currentTransition, clipboardContent.actionType); + if(clipboardContent.operation === 'cut'){ + node.name = clipboardContent.name; + }else{ + node.name = clipboardContent.name + '-copy'; } + node.domain = clipboardContent.domain; + node.actionType = clipboardContent.actionType; this.rerender(); this.doValidation(); + this.scrollToNewPosition(); + }, + deleteNode(node){ + this.deleteWorkflowNode(node); }, openEditor(node){ - this.set('showActionEditor', true); - this.set('currentAction', node.actionType); - var domain = node.getNodeDetail(); - this.set('clonedDomain',Ember.copy(domain)); - this.set('clonedErrorNode', node.errorNode); - this.set('clonedKillMessage',node.get('killMessage')); - node.set("domain", domain); - this.set('currentNode', node); + this.openWorkflowEditor(node); + }, + setFilePath(filePath){ + this.set("workflowFilePath", filePath); + }, + showNotification(node){ + this.set("showNotificationPanel", true); + if(node.actionType){ + //this.set("hoveredWidget", node.actionType+"-action-info"); + //this.set("hoveredAction", node.getNodeDetail()); + } + }, + hideNotification(){ + this.set("showNotificationPanel", false); }, addBranch(node){ - this.get("workflow").addBranch(node); - this.rerender(); + this.addWorkflowBranch(node); }, addDecisionBranch(settings){ + this.createSnapshot(); this.get("workflow").addDecisionBranch(settings); this.rerender(); }, - addKillNode(errorNode){ + setNodeTransitions(transition){ var currentNode= this.get("currentNode"); - if(errorNode && errorNode.isNew){ - this.get("workflow").addKillNode(currentNode,errorNode); - this.get("workflow.killNodes").push(errorNode); + if(transition.errorNode && transition.errorNode.isNew){ + this.get("workflow").addKillNode(currentNode,transition.errorNode); + this.get("workflow.killNodes").push(transition.errorNode); }else { - this.set('currentNode.errorNode', errorNode); + this.set('currentNode.errorNode', transition.errorNode); } + currentNode.transitions.forEach((trans)=>{ + if(transition.okToNode){ + if(trans.targetNode.id !== transition.okToNode.id){ + trans.targetNode = transition.okToNode; + this.showUndo('transition'); + } + } + }, this); }, submitWorkflow(){ + this.set('dryrun', false); + this.openJobConfig(); + }, + saveWorkflow(){ + this.set('dryrun', false); + this.openSaveWorkflow(); + }, + previewWorkflow(){ + this.set("showingPreview",false); this.get('workflowContext').clearErrors(); var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"), workflowContext:this.get('workflowContext')}); @@ -532,15 +637,11 @@ export default Ember.Component.extend(EmberValidations,{ if(this.get('workflowContext').hasErrors()){ this.set('errors',this.get('workflowContext').getErrors()); }else{ - var dynamicProperties = this.get('propertyExtractor').getDynamicProperties(workflowXml); - var configForSubmit={props:dynamicProperties,xml:workflowXml,params:this.get('workflow.parameters')}; - this.set("workflowSubmitConfigs",configForSubmit); - this.set("showingWorkflowConfigProps",true); + this.set("previewXml",vkbeautify.xml(workflowXml)); + this.set("showingPreview",true); } - }, - previewWorkflow(){ - this.set("showingPreview",false); + downloadWorkflowXml(){ this.get('workflowContext').clearErrors(); var workflowGenerator=WorkflowGenerator.create({workflow:this.get("workflow"), workflowContext:this.get('workflowContext')}); @@ -548,12 +649,15 @@ export default Ember.Component.extend(EmberValidations,{ if(this.get('workflowContext').hasErrors()){ this.set('errors',this.get('workflowContext').getErrors()); }else{ - this.set("previewXml",vkbeautify.xml(workflowXml)); - this.set("showingPreview",true); + this.workflowXmlDownload(workflowXml); } }, closeWorkflowSubmitConfigs(){ this.set("showingWorkflowConfigProps",false); + this.set("showingSaveWorkflow",false); + }, + closeSaveWorkflow(){ + this.set("showingSaveWorkflow",false); }, importWorkflowTest(){ var deferred = this.importSampleWorkflow(); @@ -563,6 +667,7 @@ export default Ember.Component.extend(EmberValidations,{ this.rerender(); this.doValidation(); }.bind(this)).catch(function(e){ + console.error(e); }); }, closeFileBrowser(){ @@ -625,7 +730,11 @@ export default Ember.Component.extend(EmberValidations,{ this.resetZoomLevel(); this.$("#flow-designer").css("transform", "scale(" + 1 + ")"); }, + resetLayout() { + this.flowRenderer.resetLayout(); + }, closeActionEditor (isSaved){ + this.send("hideNotification"); if(isSaved){ this.currentNode.onSave(); this.doValidation(); @@ -638,6 +747,27 @@ export default Ember.Component.extend(EmberValidations,{ } this.set('showActionEditor', false); this.rerender(); + }, + saveDraft(){ + this.persistWorkInProgress(); + }, + + undoDelete () { + var workflowImporter = WorkflowJsonImporter.create({}); + var workflow = workflowImporter.importWorkflow(this.get('workflowSnapshot')); + this.resetDesigner(); + this.set("workflow", workflow); + this.rerender(); + this.doValidation(); + this.set('undoAvailable', false); + }, + + registerAddBranchAction(component){ + this.set("addBranchListener",component); + }, + dryRunWorkflow(){ + this.set('dryrun', true); + this.openJobConfig(); } } });
http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js new file mode 100644 index 0000000..73cabac --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action-info.js @@ -0,0 +1,26 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +import Ember from 'ember'; +export default Ember.Component.extend({ + actions : { + hideNotification(){ + this.sendAction("hideNotification"); + } + } +}); + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js index 98a292d..03acbf3 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/fs-action.js @@ -15,11 +15,19 @@ * limitations under the License. */ import Ember from 'ember'; -import EmberValidations, { - validator -} from 'ember-validations'; +import { validator, buildValidations } from 'ember-cp-validations'; -export default Ember.Component.extend(EmberValidations, { +const Validations = buildValidations({ + 'actionModel.fsOps': { + validators: [ + validator('fs-action-validator', { + dependentKeys: ['actionModel.fsOps.@each'] + }) + ] + } +}); + +export default Ember.Component.extend(Validations, { fileBrowser: Ember.inject.service('file-browser'), setUp: function() { if (this.get('actionModel.fsOps') === undefined) { @@ -29,6 +37,8 @@ export default Ember.Component.extend(EmberValidations, { this.set("actionModel.configuration", {}); this.set("actionModel.configuration.property", Ember.A([])); } + var field = 'validations.attrs.actionModel.fsOps.isDirty'; + this.set(field, false); this.sendAction('register', 'fsAction', this); }.on('init'), initialize: function() { @@ -41,52 +51,6 @@ export default Ember.Component.extend(EmberValidations, { this.$('#collapseOne').collapse('show'); } }.on('didUpdate'), - validations: { - 'actionModel': { - inline: validator(function() { - var isValidated = true, - msg = ""; - if (!this.get('actionModel.fsOps')) { - return; - } - this.get('actionModel.fsOps').forEach(function(item, index) { - switch (item.type) { - case "mkdir": - case "delete": - case "touchz": - if (!item.settings.path) { - isValidated = false; - msg = "path is mandatory"; - } - break; - case "chmod": - if (!item.settings.path) { - isValidated = false; - msg = "path and permissions are mandatory"; - } - break; - case "chgrp": - if (!item.settings.path || !item.settings.group) { - isValidated = false; - msg = "path and group are mandatory"; - } - break; - case "move": - if (!item.settings.source || !item.settings.target) { - isValidated = false; - msg = "source and target are mandatory"; - } - break; - } - }); - if (!isValidated) { - return " "; - } - - }) - } - }, - actions: { openFileBrowser(model, context) { if (undefined === context) { http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js new file mode 100644 index 0000000..caf23b4 --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/fsaction-info.js @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +import Ember from 'ember'; + +export default Ember.Component.extend({ +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js index fc5fd37..c9be41a 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hdfs-browser.js @@ -57,7 +57,7 @@ export default Ember.Component.extend({ }, createFolder(){ var self=this; - var $elem=this.$("#selectedPath"); + var $elem=this.$('input[name="selectedPath"]'); //$elem.val($elem.val()+"/"); var folderHint="<enter folder here>"; this.set("selectedPath",this.get("selectedPath")+"/"+folderHint); @@ -98,7 +98,8 @@ export default Ember.Component.extend({ this.showNotification({ "type": "error", "message": "Upload Failed", - "details":textStatus + "details":textStatus, + "errorThrown":errorThrown }); }, uploadProgress(e){ http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js new file mode 100644 index 0000000..73cabac --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action-info.js @@ -0,0 +1,26 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +import Ember from 'ember'; +export default Ember.Component.extend({ + actions : { + hideNotification(){ + this.sendAction("hideNotification"); + } + } +}); + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js index 125aac3..ac85a9a 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive-action.js @@ -16,9 +16,25 @@ */ import Ember from 'ember'; -import EmberValidations from 'ember-validations'; +import { validator, buildValidations } from 'ember-cp-validations'; -export default Ember.Component.extend(EmberValidations,{ +const Validations = buildValidations({ + 'actionModel.script': validator('presence', { + presence : true, + disabled(model, attribute) { + return !model.get('isScript'); + }, + dependentKeys : ['isScript'] + }), + 'actionModel.query': validator('presence', { + presence : true, + disabled(model, attribute) { + return model.get('isScript'); + }, + dependentKeys : ['isScript'] + }) +}); +export default Ember.Component.extend(Validations, { hiveOptionObserver : Ember.observer('isScript',function(){ if(this.get('isScript')){ this.set("actionModel.query", undefined); @@ -68,20 +84,6 @@ export default Ember.Component.extend(EmberValidations,{ this.$('#collapseOne').collapse('show'); } }.on('didUpdate'), - validations : { - 'actionModel.script': { - presence: { - 'if':'isScript', - 'message' : 'You need to provide a value for Script' - } - }, - 'actionModel.query': { - presence: { - unless :'isScript', - 'message' : 'You need to provide a value for Query' - } - } - }, actions : { openFileBrowser(model, context){ if(undefined === context){ @@ -95,9 +97,9 @@ export default Ember.Component.extend(EmberValidations,{ }, onHiveOptionChange(value){ if(value === "script"){ - this.set('isScript',true); + this.set('isScript', true); }else{ - this.set('isScript',false); + this.set('isScript', false); } } } http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js new file mode 100644 index 0000000..3192c72 --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action-info.js @@ -0,0 +1,25 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ +import Ember from 'ember'; +export default Ember.Component.extend({ + actions : { + hideNotification(){ + this.sendAction("hideNotification"); + } + } +}); + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js index f8b53c5..f23cca7 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/hive2-action.js @@ -16,9 +16,30 @@ */ import Ember from 'ember'; -import EmberValidations from 'ember-validations'; +import { validator, buildValidations } from 'ember-cp-validations'; -export default Ember.Component.extend(EmberValidations,{ +const Validations = buildValidations({ + 'actionModel.script': validator('presence', { + presence : true, + disabled(model, attribute) { + return !model.get('isScript'); + }, + dependentKeys : ['isScript'] + }), + 'actionModel.query': validator('presence', { + presence : true, + disabled(model, attribute) { + return model.get('isScript'); + }, + dependentKeys : ['isScript'] + }), + 'actionModel.jdbc-url': validator('presence', { + presence : true + }) + +}); + +export default Ember.Component.extend(Validations,{ hiveOptionObserver : Ember.observer('isScript',function(){ if(this.get('isScript')){ this.set("actionModel.query", undefined); @@ -68,25 +89,6 @@ export default Ember.Component.extend(EmberValidations,{ this.$('#collapseOne').collapse('show'); } }.on('didUpdate'), - validations : { - 'actionModel.script': { - presence: { - 'if':'isScript', - 'message' : 'You need to provide a value for Script' - } - }, - 'actionModel.query': { - presence: { - unless :'isScript', - 'message' : 'You need to provide a value for Query' - } - }, - 'actionModel.jdbc-url': { - presence: { - 'message' : 'You need to provide a value for jdbc url' - } - } - }, actions : { openFileBrowser(model, context){ if(undefined === context){ http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js new file mode 100644 index 0000000..73cabac --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/info-header.js @@ -0,0 +1,26 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +import Ember from 'ember'; +export default Ember.Component.extend({ + actions : { + hideNotification(){ + this.sendAction("hideNotification"); + } + } +}); + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js new file mode 100644 index 0000000..c46a37f --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/instance-list-config.js @@ -0,0 +1,54 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +import Ember from 'ember'; + +export default Ember.Component.extend({ + multivalued : true, + instance : { + value : '', + displayValue : '', + type : 'date' + }, + initialize : function(){ + this.sendAction('register', this, this); + this.on('bindInputPlaceholder',function () { + this.set('addUnboundValue', true); + }.bind(this)); + }.on('init'), + bindInputPlaceholder : function () { + if(this.get('addUnboundValue') && !Ember.isBlank(this.get('instance'))){ + this.addinstance(); + } + }.on('willDestroyElement'), + addInstance (){ + this.get('instances').pushObject(Ember.copy(this.get('instance'))); + this.set('instance', { + value : '', + displayValue : '', + type : 'date' + }); + }, + actions : { + addInstance () { + this.addInstance(); + }, + deleteInstance (index) { + this.get('instances').removeAt(index); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js new file mode 100644 index 0000000..3192c72 --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action-info.js @@ -0,0 +1,25 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ +import Ember from 'ember'; +export default Ember.Component.extend({ + actions : { + hideNotification(){ + this.sendAction("hideNotification"); + } + } +}); + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js index 873c284..9d43fe7 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/java-action.js @@ -16,9 +16,18 @@ */ import Ember from 'ember'; -import EmberValidations from 'ember-validations'; +import { validator, buildValidations } from 'ember-cp-validations'; -export default Ember.Component.extend(EmberValidations, { +const Validations = buildValidations({ + 'actionModel.mainClass': validator('presence', { + presence : true + }), + 'actionModel.jobTracker': validator('presence', { + presence : true + }) +}); + +export default Ember.Component.extend(Validations, { fileBrowser : Ember.inject.service('file-browser'), javaOptsObserver : Ember.observer('isSingle',function(){ if(this.get('isSingle')){ @@ -67,18 +76,6 @@ export default Ember.Component.extend(EmberValidations, { this.$('#collapseOne').collapse('show'); } }.on('didUpdate'), - validations : { - 'actionModel.mainClass': { - presence: { - 'message' : 'You need to provide a value for Main Class', - }, - format: { - with: /([a-z][a-z_0-9]*\.)*[A-Za-z_]($[A-Za-z_]|[\w_])*/, - allowBlank: false, - message: 'You need to provide a valid value' - } - } - }, actions : { openFileBrowser(model, context){ if(undefined === context){ http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js new file mode 100644 index 0000000..4fa2666 --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-config.js @@ -0,0 +1,303 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ + +import Ember from 'ember'; +import Constants from '../utils/constants'; +import { validator, buildValidations } from 'ember-cp-validations'; + +const Validations = buildValidations({ + 'filePath': validator('presence', { + presence : true + }), + 'configMap': { + validators: [ + validator('job-params-validator', { + dependentKeys: ['[email protected]', 'showErrorMessage'] + }) + ] + } +}); + + +export default Ember.Component.extend(Validations, { + systemConfigs : Ember.A([]), + showingFileBrowser : false, + jobXml : "", + overwritePath : false, + configMap : Ember.A([]), + configPropsExists : false, + savingInProgress : false, + isStackTraceVisible: false, + isStackTraceAvailable: false, + alertType : "", + alertMessage : "", + alertDetails : "", + filePath : "", + showErrorMessage: false, + displayName : Ember.computed('type', function(){ + if(this.get('type') === 'wf'){ + return "Workflow"; + }else if(this.get('type') === 'coord'){ + return "Coordinator"; + }else{ + return "Bundle"; + } + }), + initialize :function(){ + this.set("configPropsExists", this.get("jobConfigs").props.size>0); + var configProperties = []; + configProperties.pushObjects(this.extractJobParams()); + configProperties.pushObjects(this.extractJobProperties()); + this.configureExecutionSettings(); + this.set("configMap", configProperties); + this.set("jobXml", this.get("jobConfigs").xml); + this.set('filePath', Ember.copy(this.get('jobFilePath'))); + Object.keys(this.get('validations.attrs')).forEach((attr)=>{ + var field = 'validations.attrs.'+attr+'.isDirty'; + this.set(field, false); + }, this); + }.on('init'), + rendered : function(){ + this.$("#configureJob").on('hidden.bs.modal', function () { + this.sendAction('closeJobConfigs'); + }.bind(this)); + this.$("#configureJob").modal("show"); + }.on('didInsertElement'), + extractJobParams(){ + var params = []; + var jobParams = this.get("jobConfigs").params; + if(jobParams && jobParams.configuration && jobParams.configuration.property){ + jobParams.configuration.property.forEach((param)=>{ + if(param && !param.value){ + var prop= Ember.Object.create({ + name: param.name, + value: null, + isRequired : true + }); + params.push(prop); + } + }); + } + return params; + }, + + extractJobProperties(){ + var jobProperties = []; + var jobParams = this.get("jobConfigs").params; + this.get("jobConfigs").props.forEach(function(value) { + if (value!== Constants.defaultNameNodeValue && value!==Constants.rmDefaultValue){ + var propName = value.trim().substring(2, value.length-1); + var isRequired = true; + if(jobParams && jobParams.configuration && jobParams.configuration.property){ + var param = jobParams.configuration.property.findBy('name', propName); + if(param && param.value){ + isRequired = false; + }else { + isRequired = true; + } + } + var prop= Ember.Object.create({ + name: propName, + value: null, + isRequired : isRequired + }); + jobProperties.push(prop); + } + }); + return jobProperties; + }, + configureExecutionSettings (){ + this.set('systemConfigs', Ember.A([])); + if(this.get('type') !== 'coord' && !this.get('isDryrun')){ + this.get('systemConfigs').pushObject({displayName: 'Run on submit', name : 'runOnSubmit', value: false}); + } + this.get('systemConfigs').pushObjects([ + {displayName: 'Use system lib path', name :'useSystemLibPath', value:true}, + {displayName: 'Rerun on Failure', name : 'rerunOnFailure', value:true} + ]); + }, + showNotification(data){ + if (!data){ + return; + } + if (data.type === "success"){ + this.set("alertType", "success"); + } + if (data.type === "error"){ + this.set("alertType", "danger"); + } + this.set("alertDetails", data.details); + this.set("alertMessage", data.message); + if(data.stackTrace.length){ + this.set("stackTrace", data.stackTrace); + this.set("isStackTraceAvailable", true); + } else { + this.set("isStackTraceAvailable", false); + } + }, + prepareJobForSubmission(isDryrun){ + if(this.get('validations.isInvalid')){ + return; + }; + this.set('jobFilePath', Ember.copy(this.get('filePath'))); + var url = Ember.ENV.API_URL + "/submitJob?app.path=" + this.get("filePath") + "&overwrite=" + this.get("overwritePath"); + url = url + "&jobType=" + this.get('displayName').toUpperCase(); + var submitConfigs = this.get("configMap"); + submitConfigs.forEach(function(item) { + url = url + "&config." + item.name + "=" + item.value; + }, this); + this.get('systemConfigs').forEach((config)=>{ + if(config.name === 'runOnSubmit' && !isDryrun){ + url = url + "&oozieparam.action=start"; + }else if(config.name !== 'runOnSubmit'){ + url = url + "&oozieconfig." + config.name + "=" + config.value; + } + }); + if(isDryrun){ + url = url + "&oozieparam.action=dryrun"; + } + if ( this.get("jobConfigs").props.has("${resourceManager}")){ + url= url + "&resourceManager=useDefault"; + } + this.set("savingInProgress", true); + this.submitJob(url); + }, + submitJob(url){ + Ember.$.ajax({ + url: url, + method: "POST", + dataType: "text", + contentType: "text/plain;charset=utf-8", + beforeSend: function(request) { + request.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000)); + request.setRequestHeader("X-Requested-By", "workflow-designer"); + }, + data: this.get("jobXml"), + success: function(response) { + var result=JSON.parse(response); + this.showNotification({ + "type": "success", + "message": this.get('displayName') +" saved.", + "details": "Job id :"+result.id + }); + this.set("savingInProgress",false); + }.bind(this), + error: function(response) { + console.log(response); + this.set("savingInProgress",false); + this.set("isStackTraceVisible",true); + this.showNotification({ + "type": "error", + "message": "Error occurred while saving "+ this.get('displayName').toLowerCase(), + "details": this.getParsedErrorResponse(response), + "stackTrace": this.getStackTrace(response.responseText) + }); + }.bind(this) + }); + }, + getStackTrace(data){ + if(data){ + try{ + var stackTraceMsg = JSON.parse(data).stackTrace; + if(!stackTraceMsg){ + return ""; + } + if(stackTraceMsg instanceof Array){ + return stackTraceMsg.join("").replace(/\tat /g, '<br/> at '); + } else { + return stackTraceMsg.replace(/\tat /g, '<br/> at '); + } + } catch(err){ + return ""; + } + } + return ""; + }, + startJob (jobId){ + this.set('startingInProgress', true); + var url = [Ember.ENV.API_URL, + "/v2/job/", jobId, "?action=", 'start','&user.name=oozie' + ].join(""); + Ember.$.ajax({ + url: url, + method: 'PUT', + beforeSend: function (xhr) { + xhr.setRequestHeader("X-XSRF-HEADER", Math.round(Math.random()*100000)); + xhr.setRequestHeader("X-Requested-By", "Ambari"); + } + }).done(function(){ + this.set('startingInProgress', false); + this.showNotification({ + "type": "success", + "message": this.get('displayName')+" Started", + "details": jobId + }); + }.bind(this)).fail(function(response){ + this.set('startingInProgress', false); + this.showNotification({ + "type": "error", + "message": "Error occurred while starting "+ this.get('displayName').toLowerCase(), + "details": this.getParsedErrorResponse(response), + "stackTrace": this.getStackTrace(response.responseText) + }); + }.bind(this)); + }, + getParsedErrorResponse (response){ + var detail; + if (response.responseText && response.responseText.charAt(0)==="{"){ + var jsonResp=JSON.parse(response.responseText); + if (jsonResp.status==="workflow.oozie.error"){ + detail="Oozie error. Please check the workflow."; + }else if(jsonResp.message && jsonResp.message.indexOf("<html>") > -1){ + detail= ""; + }else{ + detail=jsonResp.message; + } + }else{ + detail=response; + } + return detail; + }, + actions: { + selectFile(){ + this.set("showingFileBrowser",true); + }, + showStackTrace(){ + this.set("isStackTraceVisible", true); + }, + hideStackTrace(){ + this.set("isStackTraceVisible", false); + }, + closeFileBrowser(){ + this.set("showingFileBrowser",false); + }, + dryrun(){ + this.set('showErrorMessage', true); + this.prepareJobForSubmission(true); + }, + save(){ + this.set('showErrorMessage', true); + this.prepareJobForSubmission(false); + }, + previewXml(){ + this.set("showingPreview",true); + }, + closePreview(){ + this.set("showingPreview",false); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js index ce78e59..e403dc4 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/job-details.js @@ -16,8 +16,12 @@ */ import Ember from 'ember'; +import {WorkflowImporter} from '../domain/workflow-importer'; +import {ActionTypeResolver} from "../domain/action-type-resolver"; export default Ember.Component.extend({ + workflowImporter: WorkflowImporter.create({}), + actionTypeResolver: ActionTypeResolver.create({}), error : {}, errorMessage : Ember.computed('error', function() { if(this.get('error').status === 400){ @@ -57,85 +61,342 @@ export default Ember.Component.extend({ }), initialize : function(){ if(this.get('currentTab')){ - this.$('.nav-tabs a[href="'+this.get('currentTab').attr("href")+'"]').tab('show'); + this.$('.nav-tabs a[href="'+this.get('currentTab').attr("href")+'"]').click(); if(this.get('model.actions')){ this.set('model.actionDetails', this.get('model.actions')[0]); } } + + var x2js = new X2JS(); + var configurationObj = x2js.xml_str2json(this.get('model.conf')); + this.set('model.configurationProperties', configurationObj.configuration.property); + this.$('.nav-tabs').on('shown.bs.tab', function(event){ this.sendAction('onTabChange', this.$(event.target)); }.bind(this)); }.on('didInsertElement'), - actions : { - back (){ - this.sendAction('back'); - }, - close : function(){ - this.sendAction('close'); - }, - doRefresh : function(){ - this.sendAction('doRefresh'); - }, - getJobDefinition : function () { - Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=definition&timezone=GMT',function(response){ - this.set('model.jobDefinition', (new XMLSerializer()).serializeToString(response).trim()); - }.bind(this)).fail(function(error){ - this.set('error',error); - }.bind(this)); - }, - showFirstActionDetail : function(){ - this.set('model.actionDetails', this.get('model.actions')[0]); - }, - getJobLog : function (params){ - var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=log'; - if(params && params.logFilter){ - url = url + '&logfilter=' + params.logFilter; + + getShape(nodeType) { + switch(nodeType) { + case 'start' : + case 'end' : + case 'kill' : + case 'fork' : + case 'join' : + return 'ellipse'; + case 'action' : + return 'roundrectangle'; + case 'decision' : + return 'diamond'; + default : + return 'star'; + } + }, + + getNodeActionByName(workflowActions, nodeName) { + if (Ember.isArray(workflowActions)) { + var actionInfo = workflowActions.filter(function (modelAction) { + return modelAction.name === nodeName; + }); + return actionInfo.length>0 ? actionInfo[0] : null; + } else { + return workflowActions; + } + }, + + getActionStatus(nodeName, nodeType) { + if (nodeType === 'start') { + nodeName = ':start:'; + } + var nodeAction = this.getNodeActionByName(this.get('model.actions'), nodeName); + + if (nodeAction) { + return nodeAction.status; + } + return "Not-Visited"; + }, + + getNodeActionType(workflowXmlString, nodeType, nodeName) { + if (nodeType !== 'action') { + return nodeType; + } + + var workflowJson = new X2JS().xml_str2json(workflowXmlString); + + if (Ember.isArray(workflowJson['workflow-app'].action)) { + var workflowActionJson = workflowJson['workflow-app'].action.filter(function (workflowAction) { + return workflowAction._name === nodeName; + }); + if (workflowActionJson.length>0) { + return this.actionTypeResolver.getActionType(workflowActionJson[0]); } - if(params && params.logActionList){ - url = url + '&type=action&scope='+ params.logActionList; + } else { + return this.actionTypeResolver.getActionType(workflowJson['workflow-app'].action); + } + return ''; + }, + + getBgColorBasedOnStatus(nodeStatus) { + switch(nodeStatus) { + case 'Not-Visited' : + return '#ffffff'; + default : + return '#e6e6e6'; + } + }, + + getFontColorBasedOnStatus(nodeStatus) { + switch(nodeStatus) { + case 'ERROR' : + case 'KILLED' : + case 'FAILED' : + return '#a94442'; + default : + return '#262626'; + } + }, + + getBorderColorBasedOnStatus(nodeStatus) { + switch(nodeStatus) { + case 'OK' : + return '#5bb75b'; + case 'ERROR' : + case 'KILLED' : + case 'FAILED' : + return '#a94442'; + default : + return '#808080'; + } + }, + + getNodeLabelContent(nodeType) { + switch(nodeType) { + case 'fork' : + return '\uf0e8'; + case 'join' : + return '\uf0e8'; + default : + return ''; + } + }, + + getCyDataNodes(workflow){ + var dataNodes = []; + var self=this; + workflow.nodeVisitor.process(workflow.startNode, function(node) { + if (node.type === 'kill') { + return; } - Ember.$.get(url,function(response){ - this.set('model.jobLog', response); - }.bind(this)).fail(function(error){ - this.set('error', error); - }.bind(this)); + var nodeActionStatus = self.getActionStatus(node.name, node.type); + dataNodes.push({ data: + { id: node.id, name: node.name, type: node.type, + content: node.name + self.getNodeLabelContent(node.type), + shape: self.getShape(node.type), + bgColor: self.getBgColorBasedOnStatus(nodeActionStatus), + fontColor: self.getFontColorBasedOnStatus(nodeActionStatus), + borderColor: self.getBorderColorBasedOnStatus(nodeActionStatus) } + }); + if (node.transitions.length > 0) { + node.transitions.forEach(function(tran){ + if (tran.targetNode.type === 'kill') { + return; + } + dataNodes.push( + { + data: { + id: tran.sourceNodeId + '_to_' + tran.targetNode.id, + source:tran.sourceNodeId, + target: tran.targetNode.id, + borderColor: (self.getActionStatus(tran.targetNode.name, tran.targetNode.type) === 'Not-Visited') + ? '#808080' : self.getBorderColorBasedOnStatus(nodeActionStatus) + } + } + ); + }); + } + }); + return dataNodes; }, - getErrorLog : function (){ - Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=errorlog',function(response){ - this.set('model.errorLog', response); - }.bind(this)).fail(function(error){ - this.set('error', error); - }.bind(this)); + showActionNodeDetail(node, workflowXmlString){ + var nodeName = node.data().name; + if (nodeName === 'Start') { + nodeName = ':start:'; + } + this.set('model.nodeName', nodeName); + this.set('model.nodeType', this.getNodeActionType(workflowXmlString, node.data().type, nodeName)); + this.set('model.actionDetails', null); + var actionInfo = this.getNodeActionByName(this.get('model.actions'), nodeName); + this.set('model.actionInfo', actionInfo); }, - getAuditLog : function (){ - Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=auditlog',function(response){ - this.set('model.auditLog', response); - }.bind(this)).fail(function(error){ - this.set('error', error); + renderDag(xmlString){ + var workflow = this.get("workflowImporter").importWorkflow(xmlString); + console.log("Workflow Object..", workflow); + var dataNodes=this.getCyDataNodes(workflow); + var cy = cytoscape({ + container: document.getElementById('cy'), + elements: dataNodes, + style: [ + { + selector: 'node', + style: { + shape: 'data(shape)', + 'color': 'data(fontColor)', + 'background-color': 'data(bgColor)', + 'border-width': 1, + 'border-color': 'data(borderColor)', + label: 'data(name)', + 'text-valign': 'center', + 'font-size': 8 + } + }, + { + selector: 'node[shape = "roundrectangle"]', + style: { + width: 100, + 'border-radius': 1, + } + }, + { + selector: 'node[type = "fork"]', + style: { + 'background-image': 'assets/sitemap.png', + 'text-halign': 'left' + } + }, + { + selector: 'node[type = "join"]', + style: { + 'background-image': 'assets/join.png', + 'text-halign': 'left' + } + }, + { + selector: 'edge', + style: { + width: 1, + 'line-color': 'data(borderColor)', + 'curve-style': 'bezier', + 'target-arrow-shape': 'triangle', + 'target-arrow-color': 'data(borderColor)' + } + } + ], + layout: { + name: 'dagre' + } + }); + + // the default values of each option are outlined below: + var defaults = { + zoomFactor: 2.0, // zoom factor per zoom tick + minZoom: 0.1, // min zoom level + maxZoom: 10, // max zoom level + + // icon class names + sliderHandleIcon: 'fa fa-minus', + zoomInIcon: 'fa fa-plus', + zoomOutIcon: 'fa fa-minus', + resetIcon: 'fa fa-expand' + }; + + cy.panzoom( defaults ); + + cy.on('click', 'node', function(event) { + var node = event.cyTarget; + this.showActionNodeDetail(node, xmlString); }.bind(this)); }, - getJobDag : function (){ - this.set('model.jobDag', Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=graph'); + importSampleWorkflow (){ + var self=this; + Ember.$.ajax({ + url: "/sampledata/workflow.xml", + dataType: "text", + cache:false, + success: function(data) { + self.renderDag(data); + }.bind(this), + failure : function(data){ + console.error(data); + } + }); }, - getCoordActionReruns : function () { - var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=allruns&type=action'; - if(this.get('rerunActionList')){ - url = url + '&scope=' + this.get('rerunActionList'); + actions : { + back (){ + this.sendAction('back'); + }, + close : function(){ + this.sendAction('close'); + }, + doRefresh : function(){ + this.sendAction('doRefresh'); + }, + getJobDefinition : function () { + Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=definition&timezone=GMT',function(response){ + this.set('model.jobDefinition', (new XMLSerializer()).serializeToString(response).trim()); + }.bind(this)).fail(function(error){ + this.set('error',error); + }.bind(this)); + }, + showFirstActionDetail : function(){ + this.set('model.actionDetails', this.get('model.actions')[0]); + }, + getJobLog : function (params){ + var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=log'; + if(params && params.logFilter){ + url = url + '&logfilter=' + params.logFilter; + } + if(params && params.logActionList){ + url = url + '&type=action&scope='+ params.logActionList; + } + Ember.$.get(url,function(response){ + this.set('model.jobLog', response); + }.bind(this)).fail(function(error){ + this.set('error', error); + }.bind(this)); + }, + getErrorLog : function (){ + Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=errorlog',function(response){ + this.set('model.errorLog', response); + }.bind(this)).fail(function(error){ + this.set('error', error); + }.bind(this)); + }, + getAuditLog : function (){ + Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=auditlog',function(response){ + this.set('model.auditLog', response); + }.bind(this)).fail(function(error){ + this.set('error', error); + }.bind(this)); + }, + getJobDag : function (){ + //if (true) return this.importSampleWorkflow(); + Ember.$.get(Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=definition&timezone=GMT',function(response){ + var xmlString = (new XMLSerializer()).serializeToString(response).trim(); + this.renderDag(xmlString); + }.bind(this)).fail(function(error){ + this.set('error',error); + }.bind(this)); + // this.set('model.jobDag', Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=graph'); + }, + getCoordActionReruns : function () { + var url = Ember.ENV.API_URL+'/v2/job/'+this.get('id')+'?show=allruns&type=action'; + if(this.get('rerunActionList')){ + url = url + '&scope=' + this.get('rerunActionList'); + } + Ember.$.get(url, function(response){ + this.set('model.coordActionReruns', response.workflows); + }.bind(this)).fail(function(error){ + this.set('error', error); + }.bind(this)); + }, + getActionDetails : function (actionInfo) { + this.set('model.actionDetails', actionInfo); + }, + showWorkflow : function(workflowId){ + this.sendAction('showWorkflow', workflowId); + }, + showCoord : function(coordId){ + this.sendAction('showCoord', coordId); } - Ember.$.get(url, function(response){ - this.set('model.coordActionReruns', response.workflows); - }.bind(this)).fail(function(error){ - this.set('error', error); - }.bind(this)); - }, - getActionDetails : function (actionInfo) { - this.set('model.actionDetails', actionInfo); - }, - showWorkflow : function(workflowId){ - this.sendAction('showWorkflow', workflowId); - }, - showCoord : function(coordId){ - this.sendAction('showCoord', coordId); } - } -}); + }); http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js new file mode 100644 index 0000000..cc9eb1e --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-config.js @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ +import Ember from 'ember'; + +export default Ember.Component.extend({ + actions: { + createKillNode() { + this.sendAction('createKillNode'); + }, + + updateNode() { + this.sendAction('updateNode'); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js new file mode 100644 index 0000000..02660be --- /dev/null +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/killnode-manager.js @@ -0,0 +1,62 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You 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. +*/ +import Ember from 'ember'; + +export default Ember.Component.extend({ + initialize : function() { + this.set('killNode', {}); + this.set('editMode', false); + }.on('init'), + + rendered : function(){ + this.$('#killnode-manager-dialog').modal({ + backdrop: 'static', + keyboard: false + }); + this.$('#killnode-manager-dialog').modal('show'); + this.$('#killnode-manager-dialog').modal().on('hidden.bs.modal', function() { + this.sendAction('closeKillNodeManager'); + }.bind(this)); + }.on('didInsertElement'), + actions: { + deleteNode(index) { + this.get('killNodes').removeAt(index); + this.set('editMode', false); + }, + addNode() { + this.set('createKillnodeError',null); + this.set('editMode', false); + this.set('addKillNodeMode', true); + this.set('killNode', {}); + }, + editNode(index) { + this.set('createKillnodeError', null); + this.set('editMode', true); + this.set('currentKillNodeIndex', index); + var selectedKillNode = this.get('killNodes').objectAt(index); + this.set('killNode', {name : selectedKillNode.name, killMessage: selectedKillNode.killMessage}); + }, + updateNode(){ + this.set('editMode', false); + this.get('killNodes').objectAt(this.get('currentKillNodeIndex')).set('killMessage', this.get('killNode').killMessage); + this.set('killNode', {}); + }, + createKillNode() { + this.sendAction('createKillNode', this.get("killNode")); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js ---------------------------------------------------------------------- diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js b/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js index c3782a3..3eb8545 100644 --- a/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js +++ b/contrib/views/wfmanager/src/main/resources/ui/app/components/map-red-action.js @@ -16,9 +16,8 @@ */ import Ember from 'ember'; -import EmberValidations from 'ember-validations'; -export default Ember.Component.extend(EmberValidations, { +export default Ember.Component.extend({ hasStaticProps : true, fileBrowser : Ember.inject.service('file-browser'), staticProps : Ember.A([]), @@ -65,9 +64,6 @@ export default Ember.Component.extend(EmberValidations, { }.bind(this)); this.sendAction('register','mapRedAction', this); }.on('didInsertElement'), - validations : { - - }, observeError :function(){ if(this.$('#collapseOne label.text-danger').length > 0 && !this.$('#collapseOne').hasClass("in")){ this.$('#collapseOne').collapse('show');
